diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..605abe5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +__pycache__/ +*.pyc +*.egg-info/ +.venv/ +dist/ +*.tgz diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 214895d..fe529a5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,6 +4,8 @@ # By default, run each hook only in the standard pre-commit stage default_install_hook_types: [pre-commit, commit-msg] default_stages: [pre-commit] +default_language_version: + python: python3.13 # This is a template for connector pre-commit hooks repos: @@ -50,7 +52,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: diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index fcbba09..0000000 --- a/.pylintrc +++ /dev/null @@ -1,2 +0,0 @@ -[MASTER] -ignore=.venv diff --git a/LICENSE b/LICENSE index 01f84e5..86185fe 100644 --- a/LICENSE +++ b/LICENSE @@ -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. diff --git a/NOTICE b/NOTICE index 0176ec7..11a3da2 100644 --- a/NOTICE +++ b/NOTICE @@ -1,35 +1,3 @@ -Splunk SOAR App: MS Graph for Office 365 +Splunk SOAR App: msgraphoffice365 Copyright (c) 2017-2025 Splunk Inc. Third Party Software Attributions: - -@@@@============================================================================ - -Library: msal - 1.34.0 -Homepage: https://github.com/AzureAD/microsoft-authentication-library-for-python -License: MIT License -License Text: - -The MIT License (MIT) - -Copyright (c) Microsoft Corporation. -All rights reserved. - -This code is licensed under the MIT License. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files(the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions : - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/README.md b/README.md index 352ea0e..4145347 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ Publisher: Splunk
Connector Version: 4.1.0
Product Vendor: Microsoft
Product Name: Office 365 (MS Graph)
-Minimum Product Version: 6.3.0 +Minimum Product Version: 7.0.0 -This app connects to Office 365 using the MS Graph API to support investigate and generic actions related to the email messages and calendar events +This app enables MS Graph API-based email ingestion and investigative actions on Office 365 ## Playbook Backward Compatibility @@ -580,408 +580,269 @@ VARIABLE | REQUIRED | TYPE | DESCRIPTION **tenant** | required | string | Tenant ID (e.g. 1e309abf-db6c-XXXX-a1d2-XXXXXXXXXXXX) | **client_id** | required | string | Application ID | **auth_type** | required | string | Authentication type to use for connectivity | -**client_secret** | optional | password | Application Secret(required for OAuth) | +**client_secret** | optional | password | Application Secret (required for OAuth) | **certificate_thumbprint** | optional | password | Certificate Thumbprint (required for CBA) | **certificate_private_key** | optional | password | Certificate Private Key (.PEM) | **admin_access** | optional | boolean | Admin Access Required | **admin_consent** | optional | boolean | Admin Consent Already Provided (Required checked for CBA) | **scope** | optional | string | Access Scope (for use with OAuth non-admin access; space-separated) | +**retry_count** | optional | numeric | Maximum attempts to retry the API call (Default: 3) | +**retry_wait_time** | optional | numeric | Delay in seconds between retries (Default: 60) | **email_address** | optional | string | Email Address of the User (On Poll) | **folder** | optional | string | Mailbox folder name/folder path or the internal office365 folder ID to ingest (On Poll) | -**get_folder_id** | optional | boolean | Retrieve the folder ID for the provided folder name/folder path automatically and replace the folder parameter value (On Poll) | -**first_run_max_emails** | optional | numeric | Maximum Containers for scheduled polling first time | -**max_containers** | optional | numeric | Maximum Containers for scheduled polling | +**get_folder_id** | optional | boolean | Retrieve the folder ID for the provided folder name/folder path automatically | +**first_run_max_emails** | optional | numeric | Maximum emails for scheduled polling first time | +**max_containers** | optional | numeric | Maximum emails for scheduled polling | **extract_attachments** | optional | boolean | Extract Attachments | **extract_urls** | optional | boolean | Extract URLs | **extract_ips** | optional | boolean | Extract IPs | **extract_domains** | optional | boolean | Extract Domain Names | **extract_hashes** | optional | boolean | Extract Hashes | **ingest_eml** | optional | boolean | Ingest EML file for the itemAttachment | -**ingest_manner** | optional | string | How to Ingest (during ingestion, should the app get the latest emails or the oldest) | -**retry_count** | optional | numeric | Maximum attempts to retry the API call (Default: 3) | -**retry_wait_time** | optional | numeric | Delay in seconds between retries (Default: 60) | +**ingest_manner** | optional | string | How to Ingest | **extract_eml** | optional | boolean | Extract root (primary) email as Vault | ### Supported Actions -[test connectivity](#action-test-connectivity) - Use supplied credentials to generate a token with MS Graph
-[generate token](#action-generate-token) - Generate a token
+[test connectivity](#action-test-connectivity) - test connectivity
+[on poll](#action-on-poll) - on poll
+[on es poll](#action-on-es-poll) - Poll for new emails and create ES findings for each email.
+[copy email](#action-copy-email) - Copy an email to a folder
+[create folder](#action-create-folder) - Create a new mail folder
+[delete email](#action-delete-email) - Delete an email
+[delete event](#action-delete-event) - Delete an event
+[generate token](#action-generate-token) - Generates a new access token
+[get email properties](#action-get-email-properties) - Get properties of an email
+[get folder id](#action-get-folder-id) - Get the ID of a mail folder
+[get mailbox messages](#action-get-mailbox-messages) - Get messages from a mailbox folder
+[list folders](#action-list-folders) - Get the mail folder hierarchy
+[list group members](#action-list-group-members) - Get group members
+[list groups](#action-list-groups) - List all the groups in an organization, including but not limited to Office 365 groups
+[list users](#action-list-users) - Retrieve a list of users
+[move email](#action-move-email) - Move an email to a folder
[oof check](#action-oof-check) - Get user's out of office status
+[report message](#action-report-message) - Add the sender email into the report
+[send email](#action-send-email) - Send an email
+[unblock sender](#action-unblock-sender) - Remove a sender from the blocked senders list
+[update email](#action-update-email) - Update properties of an email
+[get email](#action-get-email) - Get an email from the server
[list events](#action-list-events) - List events from user or group calendar
[get rule](#action-get-rule) - Get the properties and relationships of a messageRule object
[list rules](#action-list-rules) - Get all the messageRule objects defined for the user's inbox
-[list users](#action-list-users) - Retrieve a list of users
-[list groups](#action-list-groups) - List all the groups in an organization, including but not limited to Office 365 groups
-[list group members](#action-list-group-members) - List all the members in group by group ID or group e-mail address
-[list folders](#action-list-folders) - Retrieve a list of mail folders
-[copy email](#action-copy-email) - Copy an email to a folder
-[move email](#action-move-email) - Move an email to a folder
-[delete email](#action-delete-email) - Delete an email
-[delete event](#action-delete-event) - Delete an event from user calendar
-[get email](#action-get-email) - Get an email from the server
-[get email properties](#action-get-email-properties) - Get non-standard email properties from the server
-[run query](#action-run-query) - Search emails
-[create folder](#action-create-folder) - Create a new folder
-[get folder id](#action-get-folder-id) - Get the API ID of the folder
-[send email](#action-send-email) - Sends an email with optional text rendering. Attachments are allowed a Content-ID tag for reference within the html
-[on poll](#action-on-poll) - Ingest emails from Office 365 using Graph API
-[update email](#action-update-email) - Update an email on the server
-[report message](#action-report-message) - Add the sender email into the report
-[block sender](#action-block-sender) - Add the sender email into the block list
-[unblock sender](#action-unblock-sender) - Remove the sender email from the block list
-[resolve name](#action-resolve-name) - Verify aliases and resolve display names to the appropriate user
-[get mailbox messages](#action-get-mailbox-messages) - Retrieves messages from a specified mailbox folder with advanced functionality +[resolve name](#action-resolve-name) - Resolve a name to email addresses
+[run query](#action-run-query) - Search emails in a mailbox
+[block sender](#action-block-sender) - Add a sender to the blocked senders list ## action: 'test connectivity' -Use supplied credentials to generate a token with MS Graph +test connectivity Type: **test**
Read only: **True** +Basic test for app. + #### Action Parameters No parameters are required for this action #### Action Output -No Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failure | +action_result.message | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | -## action: 'generate token' +## action: 'on poll' -Generate a token +on poll -Type: **generic**
-Read only: **False** +Type: **ingest**
+Read only: **True** + +Callback action for the on_poll ingest functionality #### Action Parameters -No parameters are required for this action +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**start_time** | optional | Start of time range, in epoch time (milliseconds). | numeric | | +**end_time** | optional | End of time range, in epoch time (milliseconds). | numeric | | +**container_count** | optional | Maximum number of container records to query for. | numeric | | +**artifact_count** | optional | Maximum number of artifact records to query for. | numeric | | +**container_id** | optional | Comma-separated list of container IDs to limit the ingestion to. | string | | #### Action Output -DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES ---------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.data | string | | | -action_result.summary | string | | | -action_result.message | string | | Token generated | -summary.total_objects | numeric | | 1 | -summary.total_objects_successful | numeric | | 1 | +No Output -## action: 'oof check' +## action: 'on es poll' -Get user's out of office status +Poll for new emails and create ES findings for each email. -Type: **investigate**
+Type: **ingest**
Read only: **True** +Callback action for the on_es_poll ingest functionality + #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**start_time** | optional | Start of time range, in epoch time (milliseconds). | numeric | | +**end_time** | optional | End of time range, in epoch time (milliseconds). | numeric | | +**container_count** | optional | Maximum number of findings to query for. | numeric | | #### Action Output -DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES ---------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | eeb3645f-df19-58a1-0e9c-ghi234cb5f6f | -action_result.data.\*.@odata.context | string | `url` | https://test.abc.com/v1.0/$metadata#users('EXAMPLEUSERID')/mailboxSettings/automaticRepliesSetting | -action_result.data.\*.@odata.etag | string | | | -action_result.data.\*.externalAudience | string | | all | -action_result.data.\*.externalReplyMessage | string | | | -action_result.data.\*.internalReplyMessage | string | | | -action_result.data.\*.scheduledEndDateTime.dateTime | string | | 2022-03-15T12:00:00.0000000 | -action_result.data.\*.scheduledEndDateTime.timeZone | string | | UTC | -action_result.data.\*.scheduledStartDateTime.dateTime | string | | 2022-03-14T12:00:00.0000000 | -action_result.data.\*.scheduledStartDateTime.timeZone | string | | UTC | -action_result.data.\*.status | string | | alwaysEnabled | -action_result.summary.events_matched | numeric | | 1 | -action_result.message | string | | Successfully retrieved out of office status | -summary.total_objects | numeric | | 1 | -summary.total_objects_successful | numeric | | 1 | +No Output -## action: 'list events' +## action: 'copy email' -List events from user or group calendar +Copy an email to a folder -Type: **investigate**
+Type: **generic**
Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**user_id** | optional | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | -**group_id** | optional | Group ID | string | `msgoffice365 group id` | -**filter** | optional | OData query to filter/search for specific results | string | | -**limit** | optional | Maximum number of events to return | numeric | | +**id** | required | Message ID to copy | string | `msgoffice365 message id` | +**email_address** | required | User's email (mailbox to copy from) | string | `email` | +**folder** | required | Destination folder name/path or ID | string | `msgoffice365 folder id` | +**get_folder_id** | optional | Retrieve folder ID from folder name/path | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.filter | string | | $filter=subject eq 'Test2' | -action_result.parameter.group_id | string | `msgoffice365 group id` | 3d9c58f8-9f38-4016-93ac-b61095f31c48 | -action_result.parameter.limit | numeric | | 20 | -action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | test@testdomain.abc.com | -action_result.data.\*.@odata.etag | string | | W/"b1MzKFCcdkuJ24Mc2VsdjwABAdhQhg==" | -action_result.data.\*.allowNewTimeProposals | boolean | | True False | -action_result.data.\*.attendee_list | string | | H-test, o365group | -action_result.data.\*.attendees.\*.emailAddress.address | string | `email` | H-test@testdomain.abc.com | -action_result.data.\*.attendees.\*.emailAddress.name | string | | H-test | -action_result.data.\*.attendees.\*.status.response | string | | none | -action_result.data.\*.attendees.\*.status.time | string | | 0001-01-01T00:00:00Z | -action_result.data.\*.attendees.\*.type | string | | required | -action_result.data.\*.body.content | string | | `\\r\\n\\r\\n\\r\\n\\r\\n
 
\\r\\n\\r\\n\\r\\n` | -action_result.data.\*.body.contentType | string | | html | -action_result.data.\*.bodyPreview | string | | | -action_result.data.\*.calendar@odata.associationLink | string | `url` | https://test.abc.com/v1.0/users('EXAMPLEUSERID')/calendars('EXAMPLECALENDERID')/$ref | -action_result.data.\*.calendar@odata.navigationLink | string | `url` | https://test.abc.com/v1.0/users('EXAMPLEUSERID')/calendars('EXAMPLECALENDERID') | -action_result.data.\*.categories.\*.name | string | | | -action_result.data.\*.changeKey | string | | b1MzKFCcdkuJ24Mc2VsdjwABAdhQhg== | -action_result.data.\*.createdDateTime | string | | 2019-10-03T09:03:42.4958512Z | -action_result.data.\*.end.dateTime | string | | 2019-10-04T15:30:00.0000000 | -action_result.data.\*.end.timeZone | string | | UTC | -action_result.data.\*.hasAttachments | boolean | | True False | -action_result.data.\*.hideAttendees | boolean | | True False | -action_result.data.\*.iCalUId | string | | 040000008200E00074C5B7101A82E00800000000347B5D74C979D5010000000000000000100000003F2152B556F23543B1B9C751CCD711A3 | -action_result.data.\*.id | string | `msgoffice365 event id` | AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAENAABvUzMoUJx2S4nbgxzZWx2PAAEB90vfAAA= | -action_result.data.\*.importance | string | | normal | -action_result.data.\*.isAllDay | boolean | | True False | -action_result.data.\*.isCancelled | boolean | | True False | -action_result.data.\*.isDraft | boolean | | True False | -action_result.data.\*.isOnlineMeeting | boolean | | True False | -action_result.data.\*.isOrganizer | boolean | | True False | -action_result.data.\*.isReminderOn | boolean | | True False | -action_result.data.\*.lastModifiedDateTime | string | | 2019-10-04T15:24:43.0639836Z | -action_result.data.\*.location.address.city | string | | City | -action_result.data.\*.location.address.countryOrRegion | string | | Country | -action_result.data.\*.location.address.postalCode | string | | 245004 | -action_result.data.\*.location.address.state | string | | State | -action_result.data.\*.location.address.street | string | | Location Address | -action_result.data.\*.location.coordinates.latitude | numeric | | 23.0011 | -action_result.data.\*.location.coordinates.longitude | numeric | | 72.4994 | -action_result.data.\*.location.displayName | string | | Test | -action_result.data.\*.location.locationType | string | | default | -action_result.data.\*.location.locationUri | string | `url` | https://www.bingapis.com/api/v6/localbusinesses/YN4070x2912827763012539383?setLang=en | -action_result.data.\*.location.uniqueId | string | | f30c3e81-78b7-4f47-8890-f60c3f57e199 | -action_result.data.\*.location.uniqueIdType | string | | unknown | -action_result.data.\*.locations.\*.address.city | string | | City | -action_result.data.\*.locations.\*.address.countryOrRegion | string | | Country | -action_result.data.\*.locations.\*.address.postalCode | string | | 245004 | -action_result.data.\*.locations.\*.address.state | string | | State | -action_result.data.\*.locations.\*.address.street | string | | Location Address | -action_result.data.\*.locations.\*.coordinates.latitude | numeric | | 23.0011 | -action_result.data.\*.locations.\*.coordinates.longitude | numeric | | 72.4994 | -action_result.data.\*.locations.\*.displayName | string | | Test Building Address Bus Stop | -action_result.data.\*.locations.\*.locationType | string | | localBusiness | -action_result.data.\*.locations.\*.locationUri | string | `url` | https://www.bingapis.com/api/v6/localbusinesses/YN4070x2912827763012539383?setLang=en | -action_result.data.\*.locations.\*.uniqueId | string | | f30c3e81-78b7-4f47-8890-f60c3f57e199 | -action_result.data.\*.locations.\*.uniqueIdType | string | | locationStore | -action_result.data.\*.occurrenceId | string | | | -action_result.data.\*.onlineMeeting | string | | | -action_result.data.\*.onlineMeetingProvider | string | | unknown | -action_result.data.\*.onlineMeetingUrl | string | `url` | | -action_result.data.\*.organizer.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.organizer.emailAddress.name | string | | Test Name | -action_result.data.\*.originalEndTimeZone | string | | Pacific Standard Time | -action_result.data.\*.originalStartTimeZone | string | | Pacific Standard Time | -action_result.data.\*.recurrence | string | | | -action_result.data.\*.reminderMinutesBeforeStart | numeric | | 15 | -action_result.data.\*.responseRequested | boolean | | False True | -action_result.data.\*.responseStatus.response | string | | organizer | -action_result.data.\*.responseStatus.time | string | | 0001-01-01T00:00:00Z | -action_result.data.\*.sensitivity | string | | normal | -action_result.data.\*.seriesMasterId | string | | | -action_result.data.\*.showAs | string | | busy | -action_result.data.\*.start.dateTime | string | | 2019-10-04T15:00:00.0000000 | -action_result.data.\*.start.timeZone | string | | UTC | -action_result.data.\*.subject | string | | New event - 1 | -action_result.data.\*.transactionId | string | | b2e47e5d-8f87-9845-c507-7be56490c432 | -action_result.data.\*.type | string | | singleInstance | -action_result.data.\*.webLink | string | `url` | https://outlook.office365.com/owa/?itemid=AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAENAABvUzMoUJx2S4nbgxzZWx2PAAEB90vfAAA%3D&exvsurl=1&path=/calendar/item | -action_result.data.locations.\*.displayName | string | | | -action_result.summary.events_matched | numeric | | 8 | -action_result.message | string | | Successfully retrieved 8 events | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.id | string | `msgoffice365 message id` | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.folder | string | `msgoffice365 folder id` | | +action_result.parameter.get_folder_id | boolean | | | +action_result.data.\*.id | string | | | +action_result.data.\*.subject | string | | | +action_result.data.\*.parentFolderId | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'get rule' +## action: 'create folder' -Get the properties and relationships of a messageRule object +Create a new mail folder -Type: **investigate**
+Type: **generic**
Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | -**rule_id** | required | Inbox rule ID | string | `msgoffice365 rule id` | +**email_address** | required | User's email address (mailbox) | string | `email` | +**folder** | required | Name of the folder to create | string | | +**parent_folder** | optional | Parent folder name/path or ID (leave empty for root) | string | | +**get_folder_id** | optional | Retrieve parent folder ID from folder name/path | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.rule_id | string | `msgoffice365 rule id` | AQAABgFGMAc= | -action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | test@testdomain.abc.com | -action_result.data.\*.@odata.context | string | | https://graph.microsoft.com/v1.0/$metadata#users('eeb3645f-df19-47a1-8e8c-fcd234cb5f6f')/mailFolders('inbox')/messageRules/$entity | -action_result.data.\*.actions_copyToFolder | string | | AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAF6qrTswAAAA== | -action_result.data.\*.actions_stopProcessingRules | boolean | | True False | -action_result.data.\*.conditions_fromAddresses_0_emailAddress_address | string | `email` | test@test.com | -action_result.data.\*.conditions_fromAddresses_0_emailAddress_name | string | | Ryan Edwards | -action_result.data.\*.displayName | string | | Move all messages from Casey Edwards to test-msgoffice365-test | -action_result.data.\*.hasError | boolean | | True False | -action_result.data.\*.id | string | | AQAABgFGL8A= | -action_result.data.\*.isEnabled | boolean | | True False | -action_result.data.\*.isReadOnly | boolean | | True False | -action_result.data.\*.sequence | numeric | | 2 | -action_result.summary | string | | | -action_result.message | string | | Successfully retrieved specified inbox rule | -action_result.message | string | | Successfully retrieved specified inbox rule | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.folder | string | | | +action_result.parameter.parent_folder | string | | | +action_result.parameter.get_folder_id | boolean | | | +action_result.data.\*.id | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.parentFolderId | string | | | +action_result.data.\*.childFolderCount | numeric | | | +action_result.data.\*.totalItemCount | numeric | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'list rules' +## action: 'delete email' -Get all the messageRule objects defined for the user's inbox +Delete an email -Type: **investigate**
+Type: **generic**
Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**id** | required | Message ID to delete | string | `msgoffice365 message id` | +**email_address** | required | User's email (mailbox to delete from) | string | `email` | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | test@testdomain.abc.com | -action_result.data.\*.actions.copyToFolder | string | `msgoffice365 folder id` | AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAF6qrTswAAAA== | -action_result.data.\*.actions.delete | boolean | | True False | -action_result.data.\*.actions.markAsRead | boolean | | True False | -action_result.data.\*.actions.moveToFolder | string | `msgoffice365 folder id` | AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAF6qrTtAAAAA== | -action_result.data.\*.actions.stopProcessingRules | boolean | | True False | -action_result.data.\*.conditions.fromAddresses.\*.emailAddress.address | string | `email` | test@abc.com | -action_result.data.\*.conditions.fromAddresses.\*.emailAddress.name | string | | Ryan Edwards | -action_result.data.\*.displayName | string | | Emails to Trash | -action_result.data.\*.hasError | boolean | | True False | -action_result.data.\*.id | string | `msgoffice365 rule id` | AQAABiQdmB8= | -action_result.data.\*.isEnabled | boolean | | True False | -action_result.data.\*.isReadOnly | boolean | | True False | -action_result.data.\*.sequence | numeric | | 1 | -action_result.summary.total_rules_returned | numeric | | 14 | -action_result.message | string | | Successfully retrieved 7 rules | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.id | string | `msgoffice365 message id` | | +action_result.parameter.email_address | string | `email` | | +action_result.data.\*.message | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'list users' +## action: 'delete event' -Retrieve a list of users +Delete an event -Type: **investigate**
+Type: **generic**
Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**filter** | optional | Search for specific results | string | | -**limit** | optional | Maximum number of users to return | numeric | | +**event_id** | required | Event ID to delete | string | `msgoffice365 event id` | +**user_id** | optional | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**group_id** | optional | Group ID | string | `msgoffice365 group id` | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.filter | string | | displayName eq 'User Name' | -action_result.parameter.limit | numeric | | 20 | -action_result.data.\*.businessPhones | string | | 2056120271 | -action_result.data.\*.displayName | string | | Test Admin | -action_result.data.\*.givenName | string | | Test | -action_result.data.\*.id | string | `msgoffice365 user id` | 6132ca31-7a09-434f-a269-abe836d0c01e | -action_result.data.\*.jobTitle | string | | | -action_result.data.\*.mail | string | `email` | test@testdomain.abc.com | -action_result.data.\*.mobilePhone | string | | | -action_result.data.\*.officeLocation | string | | | -action_result.data.\*.preferredLanguage | string | | | -action_result.data.\*.surname | string | | Globaltest | -action_result.data.\*.userPrincipalName | string | `msgoffice365 user principal name` `email` | test@testdomain.abc.com | -action_result.summary.total_users_returned | numeric | | 11 | -action_result.message | string | | Successfully retrieved 11 users | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.event_id | string | `msgoffice365 event id` | | +action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.parameter.group_id | string | `msgoffice365 group id` | | +action_result.data.\*.message | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'list groups' +## action: 'generate token' -List all the groups in an organization, including but not limited to Office 365 groups +Generates a new access token -Type: **investigate**
+Type: **generic**
Read only: **True** #### Action Parameters -PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS ---------- | -------- | ----------- | ---- | -------- -**filter** | optional | Search for specific results | string | | -**limit** | optional | Maximum number of groups to return | numeric | | +No parameters are required for this action #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.filter | string | | displayName eq 'Group Name' | -action_result.parameter.limit | numeric | | 20 | -action_result.data.\*.classification | string | | | -action_result.data.\*.createdDateTime | string | | 2018-09-11T09:51:07Z | -action_result.data.\*.creationOptions | string | | ExchangeProvisioningFlags:3552 | -action_result.data.\*.deletedDateTime | string | | | -action_result.data.\*.description | string | | This is for testing purpose | -action_result.data.\*.displayName | string | | Test-test-site | -action_result.data.\*.expirationDateTime | string | | | -action_result.data.\*.groupTypes | string | | Unified | -action_result.data.\*.id | string | `msgoffice365 group id` | 2a201c95-101b-42d9-a7af-9a2fdf8193f1 | -action_result.data.\*.isAssignableToRole | string | | | -action_result.data.\*.mail | string | `email` `msgoffice365 group e-mail address` `msgoffice365 group email address` | Test-test-site@testdomain.abc.com | -action_result.data.\*.mailEnabled | boolean | | True False | -action_result.data.\*.mailNickname | string | | Test-test-site | -action_result.data.\*.membershipRule | string | | | -action_result.data.\*.membershipRuleProcessingState | string | | | -action_result.data.\*.onPremisesDomainName | string | | | -action_result.data.\*.onPremisesLastSyncDateTime | string | | | -action_result.data.\*.onPremisesNetBiosName | string | | | -action_result.data.\*.onPremisesSamAccountName | string | | | -action_result.data.\*.onPremisesSecurityIdentifier | string | | | -action_result.data.\*.onPremisesSyncEnabled | string | | | -action_result.data.\*.preferredDataLocation | string | | | -action_result.data.\*.preferredLanguage | string | | | -action_result.data.\*.proxyAddresses | string | | SMTP:test-h@testdomain.abc.com | -action_result.data.\*.renewedDateTime | string | | 2018-09-11T09:51:07Z | -action_result.data.\*.resourceBehaviorOptions | string | | WelcomeEmailDisabled | -action_result.data.\*.resourceProvisioningOptions | string | | Team | -action_result.data.\*.securityEnabled | boolean | | True False | -action_result.data.\*.securityIdentifier | string | | S-1-12-1-294681889-1319597617-672379543-28952022 | -action_result.data.\*.theme | string | | | -action_result.data.\*.visibility | string | | Private | -action_result.summary.total_groups_returned | numeric | | 9 | -action_result.message | string | | Successfully retrieved 9 groups | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.data.\*.message | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'list group members' +## action: 'get email properties' -List all the members in group by group ID or group e-mail address +Get properties of an email Type: **investigate**
Read only: **True** @@ -990,1087 +851,611 @@ Read only: **True** PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**method** | required | Method to use to list group members | string | | -**identificator** | required | Group ID or group e-mail address, based on the selected method | string | `msgoffice365 group id` `msgoffice365 group email address` `msgoffice365 group e-mail address` | -**get_transitive_members** | optional | Get a list of the group's members. A group can have users, devices, organizational contacts, and other groups as members. This operation is transitive and returns a flat list of all nested members | boolean | | -**filter** | optional | Search for specific results | string | | -**limit** | optional | Maximum number of members to return | numeric | | +**id** | required | Message ID to get | string | `msgoffice365 message id` | +**email_address** | required | User's email address (mailbox) | string | `email` | +**get_headers** | optional | Get email headers | boolean | | +**get_body** | optional | Get email body | boolean | | +**get_unique_body** | optional | Get unique body (without previous replies) | boolean | | +**get_sender** | optional | Get sender information | boolean | | +**download_attachments** | optional | Download attachments | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.filter | string | | displayName eq 'Group Name' | -action_result.parameter.get_transitive_members | boolean | | True False | -action_result.parameter.method | string | | Group ID Group e-mail | -action_result.parameter.identificator | string | `msgoffice365 group id` `msgoffice365 group email address` `msgoffice365 group e-mail address` | TEST7d21-7631-4ea7-97b2-1328d1c5b901 example@test.com | -action_result.parameter.limit | numeric | | 20 | -action_result.data.\*.@odata.type | string | | #test.abc.user | -action_result.data.\*.businessPhones | string | | 2056120271 | -action_result.data.\*.displayName | string | | Test Admin | -action_result.data.\*.givenName | string | | Test | -action_result.data.\*.id | string | `msgoffice365 user id` | 6132ca31-7a09-434f-a269-abe836d0c01e | -action_result.data.\*.jobTitle | string | | | -action_result.data.\*.mail | string | `email` | test@testdomain.abc.com | -action_result.data.\*.mobilePhone | string | | | -action_result.data.\*.officeLocation | string | | | -action_result.data.\*.preferredLanguage | string | | | -action_result.data.\*.surname | string | | Globaltest | -action_result.data.\*.userPrincipalName | string | `msgoffice365 user principal name` `email` | test@testdomain.abc.com | -action_result.summary.total_members_returned | numeric | | 9 | -action_result.message | string | | Successfully retrieved 9 groups | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.id | string | `msgoffice365 message id` | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.get_headers | boolean | | | +action_result.parameter.get_body | boolean | | | +action_result.parameter.get_unique_body | boolean | | | +action_result.parameter.get_sender | boolean | | | +action_result.parameter.download_attachments | boolean | | | +action_result.data.\*.id | string | | | +action_result.data.\*.subject | string | | | +action_result.data.\*.body | string | | | +action_result.data.\*.uniqueBody | string | | | +action_result.data.\*.bodyPreview | string | | | +action_result.data.\*.sender | string | | | +action_result.data.\*.toRecipients | string | | | +action_result.data.\*.ccRecipients | string | | | +action_result.data.\*.receivedDateTime | string | | | +action_result.data.\*.hasAttachments | boolean | | True False | +action_result.data.\*.internetMessageHeaders | string | | | +action_result.data.\*.attachments | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'list folders' +## action: 'get folder id' -Retrieve a list of mail folders +Get the ID of a mail folder Type: **investigate**
Read only: **True** -If you want to list all the child folders (includes all the sub-levels) of the specific parent folder, then, you have to provide the parent folder_id parameter. If you don't provide folder_id it will list all the folders on Office 365 account (includes all the sub-level folders). - #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | -**folder_id** | optional | Parent mail folder ID | string | `msgoffice365 folder id` | +**email_address** | required | User's email address (mailbox) | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**folder** | required | Folder name or path (e.g. 'Inbox' or 'Inbox/Subfolder') | string | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.folder_id | string | `msgoffice365 folder id` | AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAD9nLiRAAA= | -action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | test@testdomain.abc.com | -action_result.data.\*.childFolderCount | numeric | | 1 | -action_result.data.\*.displayName | string | | test | -action_result.data.\*.id | string | `msgoffice365 folder id` | AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAD9nLiRAAA= | -action_result.data.\*.isHidden | boolean | | True False | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAAAAAEIAAA= | -action_result.data.\*.sizeInBytes | numeric | | 7920 | -action_result.data.\*.totalItemCount | numeric | | 0 | -action_result.data.\*.unreadItemCount | numeric | | 0 | -action_result.summary.total_folders_returned | numeric | | 14 | -action_result.message | string | | Successfully retrieved 14 mail folders | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.email_address | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.parameter.folder | string | | | +action_result.data.\*.folder_id | string | | | +action_result.data.\*.folder_name | string | | | +action_result.data.\*.display_name | string | | | +action_result.data.\*.parent_folder_id | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'copy email' - -Copy an email to a folder +## action: 'get mailbox messages' -Type: **generic**
-Read only: **False** +Get messages from a mailbox folder -The get_folder_id parameter should be enabled only when you have specified folder name/folder path in the folder parameter. If you provide folder ID in the folder parameter and set get_folder_id parameter to true, it will throw an error of folder ID not found for given folder name (because the action considers folder parameter value as folder name/folder path). The folder parameter must be either a (case sensitive) well-known name [list here; https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0] or the internal o365 folder ID. The action supports copying to a folder that is nested within another. To copy in such a folder, specify the complete folder path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate. +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**id** | required | Message ID to copy | string | `msgoffice365 message id` | -**email_address** | required | Source mailbox (email) | string | `email` | -**folder** | required | Destination folder; this must be either a (case-sensitive) well-known name or the internal o365 folder ID | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` `msgoffice365 folder id` | -**get_folder_id** | optional | Assume the folder parameter contains a folder name/folder path, separated by '/' ; i.e. Inbox/dir1/dir2/dir3. If this parameter is enabled, it retrieves the folder ID for the provided folder name/folder path automatically and replaces the parameter value | boolean | | +**email_address** | required | User's email address (mailbox) | string | `email` | +**folder** | optional | Folder name/path or ID | string | | +**get_folder_id** | optional | Retrieve folder ID from folder name/path | boolean | | +**limit** | optional | Maximum number of messages to return | numeric | | +**offset** | optional | Number of messages to skip | numeric | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.email_address | string | `email` | test@testdomain.abc.com | -action_result.parameter.folder | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` `msgoffice365 folder id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA | -action_result.parameter.get_folder_id | boolean | | True False | -action_result.parameter.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpdwAAAA== | -action_result.data.\*.@odata.context | string | `url` | https://test.abc.com/v1.0/$metadata#message | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9" | -action_result.data.\*.@odata.type | string | | #test.abc.message | -action_result.data.\*.bccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.bccRecipients.name | string | | Test Name | -action_result.data.\*.body.content | string | | `plain text?\\r\\n` | -action_result.data.\*.body.contentType | string | | text | -action_result.data.\*.bodyPreview | string | | plain text? | -action_result.data.\*.categories | string | | | -action_result.data.\*.ccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.ccRecipients.name | string | | Test Name | -action_result.data.\*.changeKey | string | | CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9 | -action_result.data.\*.conversationId | string | | AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQANDpL7xEHORGgd1idbVXqcg= | -action_result.data.\*.conversationIndex | string | | AQHW+IHb9hH4JnJtjUmniPjyy9YF2Y== | -action_result.data.\*.createdDateTime | string | | 2017-10-25T22:29:01Z | -action_result.data.\*.flag.flagStatus | string | | notFlagged | -action_result.data.\*.from.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.from.emailAddress.name | string | | Test Name | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.folder | string | | | +action_result.parameter.get_folder_id | boolean | | | +action_result.parameter.limit | numeric | | | +action_result.parameter.offset | numeric | | | +action_result.data.\*.id | string | | | +action_result.data.\*.subject | string | | | +action_result.data.\*.sender | string | | | +action_result.data.\*.receivedDateTime | string | | | +action_result.data.\*.bodyPreview | string | | | action_result.data.\*.hasAttachments | boolean | | True False | -action_result.data.\*.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAAQSl1b8BFiEmbqZql_JiUtwABUH-stgAAAA== | -action_result.data.\*.importance | string | | normal | -action_result.data.\*.inferenceClassification | string | | focused | -action_result.data.\*.internetMessageId | string | `msgoffice365 internet message id` | | -action_result.data.\*.isDeliveryReceiptRequested | boolean | | True False | -action_result.data.\*.isDraft | boolean | | True False | action_result.data.\*.isRead | boolean | | True False | -action_result.data.\*.isReadReceiptRequested | boolean | | True False | -action_result.data.\*.lastModifiedDateTime | string | | 2017-11-02T23:58:59Z | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA | -action_result.data.\*.receivedDateTime | string | | 2017-10-25T22:29:01Z | -action_result.data.\*.replyTo | string | | | -action_result.data.\*.sender.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.sender.emailAddress.name | string | | Test Name | -action_result.data.\*.sentDateTime | string | | 2017-10-25T22:28:57Z | -action_result.data.\*.subject | string | `msgoffice365 subject` | more body formats? | -action_result.data.\*.toRecipients.\*.emailAddress.address | string | `email` | Test@testdomain.abc.com | -action_result.data.\*.toRecipients.\*.emailAddress.name | string | | Test Name | -action_result.data.\*.webLink | string | `url` | https://outlook.office365.com/owa/?ItemID=AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0%2BMMHKonvdoWQcAQSl1b8BFiEmbqZql%2BJiUtwAAAgEbAAAAQSl1b8BFiEmbqZql%2BJiUtwABUH%2FstgAAAA%3D%3D&exvsurl=1&viewmodel=ReadMessageItem | -action_result.summary | string | | | -action_result.message | string | | Successfully copied email | +action_result.data.\*.importance | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'move email' - -Move an email to a folder +## action: 'list folders' -Type: **generic**
-Read only: **False** +Get the mail folder hierarchy -The get_folder_id parameter should be enabled only when you have specified folder name/folder path in the folder parameter. If you provide folder ID in the folder parameter and set get_folder_id parameter to true, it will throw an error of folder ID not found for given folder name (because the action considers folder parameter value as folder name/folder path). The folder parameter must be either a (case sensitive) well-known name [list here; https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0] or the internal o365 folder ID. The action supports moving to a folder that is nested within another. To copy in such a folder, specify the complete folder path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate. +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**id** | required | Message ID to move | string | `msgoffice365 message id` | -**email_address** | required | Source mailbox (email) | string | `email` | -**folder** | required | Destination folder; this must be either a (case-sensitive) well-known name or the internal o365 folder ID | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` `msgoffice365 folder id` | -**get_folder_id** | optional | Assume the folder parameter contains a folder name/folder path, separated by '/'(forward slash) ; i.e. Inbox/dir1/dir2/dir3. If this parameter is enabled, it retrieves the folder ID for the provided folder name/folder path automatically and replaces the parameter value | boolean | | +**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**folder_id** | optional | Parent mail folder id or well-known name | string | `msgoffice365 folder id` | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.email_address | string | `email` | test@testdomain.abc.com | -action_result.parameter.folder | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` `msgoffice365 folder id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA | -action_result.parameter.get_folder_id | boolean | | True False | -action_result.parameter.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpdwAAAA== | -action_result.data.\*.@odata.context | string | `url` | https://test.abc.com/v1.0/$metadata#message | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9" | -action_result.data.\*.@odata.type | string | | #test.abc.message | -action_result.data.\*.bccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.bccRecipients.name | string | | Test User | -action_result.data.\*.body.content | string | | `plain text?\\r\\n` | -action_result.data.\*.body.contentType | string | | text | -action_result.data.\*.bodyPreview | string | | plain text? | -action_result.data.\*.categories | string | | | -action_result.data.\*.ccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.ccRecipients.name | string | | Test Name | -action_result.data.\*.changeKey | string | | CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9 | -action_result.data.\*.conversationId | string | | AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQANDpL7xEHORGgd1idbVXqcg= | -action_result.data.\*.conversationIndex | string | | AQHW+IHb9hH4JnJtjUmniPjyy9YF1Y== | -action_result.data.\*.createdDateTime | string | | 2017-10-25T22:29:01Z | -action_result.data.\*.flag.flagStatus | string | | notFlagged | -action_result.data.\*.from.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.from.emailAddress.name | string | | Test Name | -action_result.data.\*.hasAttachments | boolean | | True False | -action_result.data.\*.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAAQSl1b8BFiEmbqZql_JiUtwABUH-stgAAAA== | -action_result.data.\*.importance | string | | normal | -action_result.data.\*.inferenceClassification | string | | focused | -action_result.data.\*.internetMessageId | string | `msgoffice365 internet message id` | | -action_result.data.\*.isDeliveryReceiptRequested | boolean | | True False | -action_result.data.\*.isDraft | boolean | | True False | -action_result.data.\*.isRead | boolean | | True False | -action_result.data.\*.isReadReceiptRequested | boolean | | True False | -action_result.data.\*.lastModifiedDateTime | string | | 2017-11-02T23:58:59Z | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA | -action_result.data.\*.receivedDateTime | string | | 2017-10-25T22:29:01Z | -action_result.data.\*.replyTo | string | | | -action_result.data.\*.sender.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.sender.emailAddress.name | string | | Test Name | -action_result.data.\*.sentDateTime | string | | 2017-10-25T22:28:57Z | -action_result.data.\*.subject | string | `msgoffice365 subject` | more body formats? | -action_result.data.\*.toRecipients.\*.emailAddress.address | string | `email` | Test@testdomain.abc.com | -action_result.data.\*.toRecipients.\*.emailAddress.name | string | | Test Name | -action_result.data.\*.webLink | string | `url` | https://outlook.office365.com/owa/?ItemID=AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0%2BMMHKonvdoWQcAQSl1b8BFiEmbqZql%2BJiUtwAAAgEbAAAAQSl1b8BFiEmbqZql%2BJiUtwABUH%2FstgAAAA%3D%3D&exvsurl=1&viewmodel=ReadMessageItem | -action_result.summary | string | | | -action_result.message | string | | Successfully moved email | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.parameter.folder_id | string | `msgoffice365 folder id` | | +action_result.data.\*.id | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.parentFolderId | string | | | +action_result.data.\*.childFolderCount | numeric | | | +action_result.data.\*.unreadItemCount | numeric | | | +action_result.data.\*.totalItemCount | numeric | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'delete email' +## action: 'list group members' -Delete an email +Get group members -Type: **contain**
-Read only: **False** +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**id** | required | Message ID to delete | string | `msgoffice365 message id` | -**email_address** | required | Email address of the mailbox owner | string | `email` | +**group_id** | required | Group ID | string | `msgoffice365 group id` | +**limit** | optional | Maximum number of members to return | numeric | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.email_address | string | `email` | test@testdomain.abc.com | -action_result.parameter.id | string | `msgoffice365 message id` | AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAEMAABvUzMoUJx2S4nbgxzZWx2PAAEIbt7NAAA= | -action_result.data | string | | | -action_result.summary | string | | | -action_result.message | string | | Successfully deleted email | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.group_id | string | `msgoffice365 group id` | | +action_result.parameter.limit | numeric | | | +action_result.data.\*.id | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.mail | string | | | +action_result.data.\*.userPrincipalName | string | | | +action_result.data.\*.userType | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'delete event' +## action: 'list groups' -Delete an event from user calendar +List all the groups in an organization, including but not limited to Office 365 groups -Type: **generic**
-Read only: **False** +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**id** | required | Event ID to delete | string | `msgoffice365 event id` | -**email_address** | required | Email address of the mailbox owner | string | `email` | -**send_decline_response** | optional | Send decline response to the organizer | boolean | | +**filter** | optional | Search for specific results | string | | +**limit** | optional | Maximum number of groups to return | numeric | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.email_address | string | `email` | test@testdomain.abc.com | -action_result.parameter.id | string | `msgoffice365 event id` | TestAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAEMAABvUzMoUJx2S4nbgxzZWx2PAAEIbt7NAAA= | -action_result.parameter.send_decline_response | boolean | | True False | -action_result.data | string | | | -action_result.summary | string | | | -action_result.message | string | | Successfully deleted email | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.filter | string | | | +action_result.parameter.limit | numeric | | | +action_result.data.\*.id | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.description | string | | | +action_result.data.\*.mail | string | | | +action_result.data.\*.mailEnabled | boolean | | True False | +action_result.data.\*.mailNickname | string | | | +action_result.data.\*.groupTypes.\* | string | | | +action_result.data.\*.createdDateTime | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'get email' +## action: 'list users' -Get an email from the server +Retrieve a list of users Type: **investigate**
Read only: **True** -If the 'download attachments' parameter is set to true, the action will ingest the '#microsoft.graph.itemAttachment' and '#microsoft.graph.fileAttachment' type of attachments. - #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**id** | required | Message ID to get | string | `msgoffice365 message id` | -**email_address** | required | Email address of the mailbox owner | string | `email` | -**download_attachments** | optional | Download attachments to vault | boolean | | -**extract_headers** | optional | Extract email headers | boolean | | -**download_email** | optional | Download email to vault | boolean | | +**filter** | optional | Search for specific results | string | | +**limit** | optional | Maximum number of users to return | numeric | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.download_attachments | boolean | | True False | -action_result.parameter.download_email | boolean | | True False | -action_result.parameter.email_address | string | `email` | test@abc.com | -action_result.parameter.extract_headers | boolean | | True False | -action_result.parameter.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpfAAAAA== | -action_result.data.\*.@odata.context | string | `url` | https://abc.test.com/v1.0/$metadata#users('test%40abc.com')/messages/$entity | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFLoCsS" | -action_result.data.\*.@odata.type | string | | #test.abc.eventMessage | -action_result.data.\*.allowNewTimeProposals | string | | | -action_result.data.\*.attachments.\*.@odata.mediaContentType | string | | application/octet-stream | -action_result.data.\*.attachments.\*.@odata.type | string | | #test.abc.fileAttachment | -action_result.data.\*.attachments.\*.attachmentType | string | | #test.abc.fileAttachment | -action_result.data.\*.attachments.\*.contentId | string | `email` | F5832F4CF6EFEC41B9CBC6DED238A234@namprd18.prod.test.com | -action_result.data.\*.attachments.\*.contentLocation | string | | | -action_result.data.\*.attachments.\*.contentType | string | | text/plain | -action_result.data.\*.attachments.\*.id | string | | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpfAAAAAESABAA0EuDIoiFb0ifXM0ETmQMVw== | -action_result.data.\*.attachments.\*.isInline | boolean | | True False | -action_result.data.\*.attachments.\*.itemType | string | | #test.abc.message | -action_result.data.\*.attachments.\*.lastModifiedDateTime | string | | 2017-10-26T01:31:43Z | -action_result.data.\*.attachments.\*.name | string | | attachment.txt | -action_result.data.\*.attachments.\*.size | numeric | | 355 | -action_result.data.\*.attachments.\*.vaultId | string | `sha1` `vault id` | 719dbf72d7c0bc89d7e34306c08a0b66191902b9 | -action_result.data.\*.bccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.bccRecipients.name | string | | Test Name | -action_result.data.\*.body.content | string | | `Have a good time with these.\\r\\n` | -action_result.data.\*.body.contentType | string | | text | -action_result.data.\*.bodyPreview | string | | Have a good time with these. | -action_result.data.\*.categories | string | | Green category | -action_result.data.\*.ccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.ccRecipients.name | string | | Test Domain | -action_result.data.\*.changeKey | string | | CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFLoCsS | -action_result.data.\*.conversationId | string | | AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQAEU43FQ-fk5LmOxKoTpmHfw= | -action_result.data.\*.conversationIndex | string | | AQHWRVB0TS7xy6ZOSEeEl0ahrRHNfQ== | -action_result.data.\*.createdDateTime | string | | 2017-10-26T01:31:43Z | -action_result.data.\*.endDateTime.dateTime | string | | 2022-07-26T09:30:00.0000000 | -action_result.data.\*.endDateTime.timeZone | string | | UTC | -action_result.data.\*.event.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFLoCsS" | -action_result.data.\*.event.allowNewTimeProposals | boolean | | True False | -action_result.data.\*.event.attendees.\*.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.event.attendees.\*.emailAddress.name | string | | Test Name | -action_result.data.\*.event.attendees.\*.status.response | string | | none | -action_result.data.\*.event.attendees.\*.status.time | string | | 0001-01-01T00:00:00Z | -action_result.data.\*.event.attendees.\*.type | string | | required | -action_result.data.\*.event.body.content | string | | `plain text?\\r\\n` | -action_result.data.\*.event.body.contentType | string | | text | -action_result.data.\*.event.bodyPreview | string | | plain text? | -action_result.data.\*.event.calendar@odata.associationLink | string | `url` | https://test.abc.com/v1.0/users('test@user.abc.com')/calendars('EXAMPLECALENDERID')/$ref | -action_result.data.\*.event.calendar@odata.navigationLink | string | `url` | https://test.abc.com/v1.0/users('test@user.abc.com')/calendars('EXAMPLECALENDERID') | -action_result.data.\*.event.changeKey | string | | CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9 | -action_result.data.\*.event.createdDateTime | string | | 0001-01-01T00:00:00Z | -action_result.data.\*.event.end.dateTime | string | | 0001-01-01T00:00:00.0000000 | -action_result.data.\*.event.end.timeZone | string | | UTC | -action_result.data.\*.event.hasAttachments | boolean | | True False | -action_result.data.\*.event.hideAttendees | boolean | | True False | -action_result.data.\*.event.iCalUId | string | | 040000008200E00074C5B7101A82E0080000000074AC2CF9CF5ED8010000000000000000100000005B5B104FC55A6E44BC5C6A093AB7F07C | -action_result.data.\*.event.id | string | `msgoffice365 event id` | AAMkAGYwYmE5NmQ0LWVhOGItNGFhMy05OWNlLTE5MzhjMTE5YWYyMQBGAAAAAACkbmmUA5U5RZwZvvg8zew_BwDWyBbuDx-uTKpJ-DXRPByGAAAA1bxhAAAr0tXr3dtaS5qYgFGhi6QjAAQiOoS2AAA= | -action_result.data.\*.event.importance | string | | normal | -action_result.data.\*.event.isAllDay | boolean | | False True | -action_result.data.\*.event.isCancelled | boolean | | True False | -action_result.data.\*.event.isDraft | boolean | | True False | -action_result.data.\*.event.isOnlineMeeting | boolean | | True False | -action_result.data.\*.event.isOrganizer | boolean | | True False | -action_result.data.\*.event.isReminderOn | boolean | | True False | -action_result.data.\*.event.lastModifiedDateTime | string | | 0001-01-01T00:00:00Z | -action_result.data.\*.event.location.displayName | string | | Test | -action_result.data.\*.event.location.locationType | string | | default | -action_result.data.\*.event.location.uniqueIdType | string | | unknown | -action_result.data.\*.event.occurrenceId | string | | | -action_result.data.\*.event.onlineMeeting.joinUrl | string | `url` | https://test.abc.com/l/meetup-join/19%3ameeting_ZjViMTdlNjEtZjYxNi00N2QyLWJmOWYtMGU5MjVjMDM3ZTZl%40thread.v2/0?context=%7b%22Tid%22%3a%22a417c578-c7ee-480d-a225-d48057e74df5%22%2c%22Oid%22%3a%22e4c722ac-3b83-478d-8f52-c388885dc30f%22%7d | -action_result.data.\*.event.onlineMeetingProvider | string | | teamsForBusiness | -action_result.data.\*.event.onlineMeetingUrl | string | | | -action_result.data.\*.event.organizer.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.event.organizer.emailAddress.name | string | | Test Name | -action_result.data.\*.event.originalEndTimeZone | string | | UTC | -action_result.data.\*.event.originalStartTimeZone | string | | UTC | -action_result.data.\*.event.recurrence | string | | | -action_result.data.\*.event.reminderMinutesBeforeStart | numeric | | 0 | -action_result.data.\*.event.responseRequested | boolean | | True False | -action_result.data.\*.event.responseStatus.response | string | | accepted | -action_result.data.\*.event.responseStatus.time | string | | 0001-01-01T00:00:00Z | -action_result.data.\*.event.sensitivity | string | | normal | -action_result.data.\*.event.seriesMasterId | string | | | -action_result.data.\*.event.showAs | string | | tentative | -action_result.data.\*.event.start.dateTime | string | | 0001-01-01T00:00:00.0000000 | -action_result.data.\*.event.start.timeZone | string | | UTC | -action_result.data.\*.event.subject | string | | Just wanted to say hello | -action_result.data.\*.event.transactionId | string | | | -action_result.data.\*.event.type | string | | singleInstance | -action_result.data.\*.event.webLink | string | `url` | https://test.abc.com/owa?itemid=AAMkAGYwYmE5NmQ0LWVhOGItNGFhMy05OWNlLTE5MzhjMTE5YWYyMQBGAAAAAACkbmmUA5U5RZwZvvg8zew%2BBwDWyBbuDx%2FuTKpJ%2FDXRPByGAAAA1bxhAAAr0tXr3dtaS5qYgFGhi6QjAAQiOoS2AAA%3D&exvsurl=1&path=/calendar/item | -action_result.data.\*.flag.flagStatus | string | | notFlagged | -action_result.data.\*.from.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.from.emailAddress.name | string | `email` | Test Name | -action_result.data.\*.hasAttachments | boolean | | True False | -action_result.data.\*.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpfAAAAA== | -action_result.data.\*.importance | string | | normal | -action_result.data.\*.inferenceClassification | string | | focused | -action_result.data.\*.internetMessageHeaders.ARC-Authentication-Results | string | | i=1; mx.test.com 1; spf=pass smtp.mailfrom=user.test.com; dmarc=pass action=none header.from=user.abc.com; dkim=pass header.d=test.abc.com; arc=none | -action_result.data.\*.internetMessageHeaders.ARC-Message-Signature | string | | i=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=3fZ/7Rn8nzby4BCiwxGpfUUiHgfvSZ6MOHtHg1Hmixs=; b=f7VAkSO/T2NJuxMrJNBzeow/t2iTVZfAkVaZGgtAoYq3Wb9cUDEfAtTE4RmzSqLEqP2iXxLK7JsRfV2uqsbGinp3ZuRnHSqMoRzO0XCN8KjE/Z9hUyUmq05clk2rB3TqCcKK6ipy1+N+4mvCFFb6O+bN/9TGhPos1dY0X0sy33sow9oeND6nm8HvtIzp/hG0xKduPnEHwQiCCwRTmdAtbAzMWmnEyyodRQg/PCgjAPKPpeFELZ+pr/bbEkySxLCu/xY1qhoa8JMIrh1EUFHyFyscTDc580YNnowNqAR//iO8DRGaTG1Znv4MI7mlcmM/vopKYAa6zNPXb2LHDqwKBQ== | -action_result.data.\*.internetMessageHeaders.ARC-Seal | string | | i=1; a=rsa-sha256; s=arcselector9901; d=test.com; cv=none; b=GIfa87IhNn+v4Mdn75nJpk9WjELaP8fw9+C+Ey0QFktfU60rPvtJDc5qJS5mN/g6COFa2inOEc4S+Cm3R1BLcBgqkCZR/niPjMp/A2tEHs9OeY75S+T8d5OsfmFcD2jC5/59Dc8EAEKw3UnYKxCC4CKWPFCXrE+Cu3jLEtt04Izr1rXTTQacfCjwYN4OsKI9lHyn6JRDlePR2RZKztMcnt/hcOJz6cwFe+MAUgg0qjKB8p+27o2hQKu+LlQDg8nMRQ6jHkD8DylOclHe2nVKmEhNXi67PTeoqTfZe4+YgSOrnJOUdlra5q/EoWN0FnM9Zt0+0K42ncYSWcC0NpsbqQ== | -action_result.data.\*.internetMessageHeaders.Accept-Language | string | | en-US | -action_result.data.\*.internetMessageHeaders.Authentication-Results | string | | spf=pass (sender IP is 209.85.210.171) smtp.mailfrom=testdomain.com; .abc.com; dkim=pass (signature was verified) header.d=testdomain.com.20150623.gappssmtp.com;.abc.com; dmarc=pass action=none header.from=testdomain.com;compauth=pass reason=100 | -action_result.data.\*.internetMessageHeaders.Authentication-Results-Original | string | | dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=test.abc.com; | -action_result.data.\*.internetMessageHeaders.Content-Language | string | | en-US | -action_result.data.\*.internetMessageHeaders.Content-Transfer-Encoding | string | | binary | -action_result.data.\*.internetMessageHeaders.Content-Type | string | | multipart/related | -action_result.data.\*.internetMessageHeaders.DKIM-Signature | string | | v=1; a=rsa-sha256; c=relaxed/relaxed; d=testdomain.com.20150623.gappssmtp.com; s=20150623; h=message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=avAAeJ8jF08K4oIBhxTirRmyB+SXHwdU0zdxv7eqs/zWaWWcgmT0007KP560TTgo5u oD4nb6TvKxpRyWW4QwmkbuMIwHsMvehd2l1gispV3AawyGJjpmN7ErVYfLtIkz2Tap3V YxmluV+SqeyyxTU8pFAEZ7+2C2lOb1DO5TC7xCMv+dyzevSscJdbeN0dFkG+C93zCqkg w2fxubx2HDD7b/U6m2wXllYhH608wKJ/qYzyvQyqxYqNiQOtPRg2gw4sZ2UgN3+UQyVq 8ubO39ZuqakJpzEzYMw10d6E7SQhvHDJH7mFwhBlzhvOpb2gLJDN8n8dJaZo05BozQqq MsvA== | -action_result.data.\*.internetMessageHeaders.Date | string | | Thu, 18 Jun 2020 02:11:26 -0700 | -action_result.data.\*.internetMessageHeaders.From | string | | "Test" | -action_result.data.\*.internetMessageHeaders.In-Reply-To | string | | | -action_result.data.\*.internetMessageHeaders.MIME-Version | string | | 1.0 | -action_result.data.\*.internetMessageHeaders.Message-ID | string | | <5eeb2fbe.1c69fb81.22b4b.676a@mx.test.com> | -action_result.data.\*.internetMessageHeaders.Received | string | | from localhost.localdomain (host-240.test.com. [204.107.141.240]) by test.abc.com with UTF8SMTPSA id ng12sm1923252pjb.15.2020.06.18.02.11.26 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jun 2020 02:11:26 -0700 (PDT) | -action_result.data.\*.internetMessageHeaders.Received-SPF | string | | Pass (protection.test.com: domain of testdomain.com designates 209.85.210.171 as permitted sender) receiver=protection.test.com; client-ip=209.85.210.171; helo=mail-pf1-f171.test.com; | -action_result.data.\*.internetMessageHeaders.References | string | | | -action_result.data.\*.internetMessageHeaders.Return-Path | string | `email` | notifications@testdomain.com | -action_result.data.\*.internetMessageHeaders.Subject | string | | Fw: Email having different attachments | -action_result.data.\*.internetMessageHeaders.Thread-Index | string | | AQDEZLqyXR4k4Sc6skyFCMPITcMsbKpGS7At | -action_result.data.\*.internetMessageHeaders.Thread-Topic | string | | Email having different attachments | -action_result.data.\*.internetMessageHeaders.To | string | | "Test" | -action_result.data.\*.internetMessageHeaders.X-EOPAttributedMessage | string | | 0 | -action_result.data.\*.internetMessageHeaders.X-EOPTenantAttributedMessage | string | | a417c578-c7ee-480d-a225-d48057e74df5:0 | -action_result.data.\*.internetMessageHeaders.X-Forefront-Antispam-Report | string | | CIP:209.85.210.171;CTRY:US;LANG:en;SCL:-1;SRV:;IPV:NLI;SFV:SFE;H:mail-pf1-f171.test.com;PTR:mail-pf1-f171.test.com;CAT:NONE;SFTY:;SFS:;DIR:INB;SFP:; | -action_result.data.\*.internetMessageHeaders.X-Forefront-Antispam-Report-Untrusted | string | | CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BYAPR18MB2408.namprd18.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230016)(136003)(396003)(346002)(39830400003)(376002)(366004)(186003)(83380400001)(64756008)(76116006)(66946007)(71200400001)(8676002)(66476007)(66446008)(66556008)(6916009)(55016003)(91956017)(9686003)(41300700001)(6506007)(7696005)(26005)(2906002)(33656002)(38070700005)(86362001)(38100700002)(3480700007)(122000001)(5660300002)(4744005)(316002)(296002)(166002)(478600001)(8936002)(52536014);DIR:OUT;SFP:1102; | -action_result.data.\*.internetMessageHeaders.X-Gm-Message-State | string | | AOAM533ynFERIhSIewEEkj4b8B1rPNOEeie1IxBdrd55treEMtBa1jkL cO5ee4Ff6p0FYedfFtVtHKiCglGTpFTOSw== | -action_result.data.\*.internetMessageHeaders.X-Google-DKIM-Signature | string | | v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=fPT47NIiheeY6GM0bxUOlsmnOgN4WuiOlalFvZqrAiFiOoYk6zrznvgIcAtiHZ4nxE naQAa+mZs5svqRjib3YI52OvR5U8MitIYaa0Rt3LyYSUO1s3iKTUs4nHyRnqPt1skNl7 2OUwsZPXo3ShJDw/uxZRu/cuN1iIfeuE02PrbR04p4D8+1XRslqt/Xqm/bOWKUauqZWe dH1E7meFY01hXxODreO4nWHIhsZgr49TpP/OqRyFcyKHHFFg2sPGXz+QNah6jP4YQUYd Tty2wzOX3nc/YS7TkVo3ORmbzh9o+UZaqH8wHbQlyTdklYxoMPvJwZTo72rTxZeqiJ9E J7PQ== | -action_result.data.\*.internetMessageHeaders.X-Google-Smtp-Source | string | | ABdhPJxrYC7raBubCCIOmauxmxryzS9KsihTN6XCRgaNp2rDrG71TVxryzYCtelFOZ2Xj1LzcYIiMA== | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData | string | | VSM9HTzub/OH3NCwWNKQqkkzjnhdw5kXsgd9WM0SRgZ0qRdPg5D9/o3LA7lf8ziXc5k0mm9M5mHvFoYePXNXs/MGhGdBGxa/qUQ+FVHA2mDgfPkamJCEZxz//OX/uruTDo+zF4p9D1dQJpnIpx1M75OhuvrHX/BxWWzyAh78DXfF214YHdyFBCYepwl56CS7+fSGQL/r3p+OvWIBnIkISC+HJljSro2k47pPPAkspMhoUkb+zklyENFjez+JcEHYlih2FiNeUO8kb9b7qvlm3zPK98HLspzDh4BojpQ6Ff330iy7nfIK726tCMByxjOdnEQSB9Ua2sbE5gxSeeWL8MB5DHcQSSsXg+sR8w4gXrXLO3meE0lNQKRoAv2b1U0Q+yM0QBqeQWlymZG21bKeuH4gtAFQvfXNjoCtIbBQK1n7ZnL7fI21FJZRcMcKEneus6gLYUqD4PdLEq9FEGbfgiLmVYeUAL2A0Q/gectvL1OVudtHVR5gFMJKt65F1OtS04CPulfLLFSl1F4AzpjjtBSyQcK9R7bOsjoHxQXPMd9fMCzMSIq5f551pO0klKqWY7l11Un2Noj6CA7EtXiD1bTv8JmYQEKR+0HTZagNd+79GeTvKjxTvt9MkyO8k3aqWyNqT331ITnVICtksN1TVMCp8GVeDudNMr2PLSW0alOduR5unuEgTWrqHoaTGOovQx0PVjudNlpZ80ANK9hqaC/ZhLLOtNpJ3fZnjs06PzrPLGhE/IeccY1n8sYDvGm1QA9TN6JaaGPl1Pj6ecy16k0XuF/PKGHTL0M4LCpxSS6T87oFFH1zHkKtmbJp3aAI4bt3ihbQmwFb29JyMgL7ZOy+zrIwXGILh1KQGWQQv1uXXnAuqQy29HeFXs6D2hDHxHlBk5ZQ+vgRtsvRvGnq58vJ3CapjntfL3pOINUj1avLyAZxjasBWMTwaZs9JQ4ZIMekzkIk05lh9XfDSeULk2yKaH8YSCC6ENUHxSWa6pPHJfOdp9kXwOtlp09/VTTAikKy862k9ybN4bRWZB45B9Pv5scna8IX3rthIXUih8c= | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData-Original-0 | string | | SC/7FB9jcHrfqMtL+8C1Sp2MBDl48qjRn/ZzFJk8+2yWs9kmQwzxXC8TA3HuU0EMts58S8uTyFbx12+qQUJ45/B5QFEfP5j1o7B5kj1Q0C9S+LAg6YptTwxi29Ei1T4mf+CpfzIeA73NzXB7pWjnmycYYlL3TAn0xJATthWspUZ+g4jZoDzNSIdAZWqoc1j4+dHA6WR+U9zZi0vODxRmplSWq2FeO8uMKqD43StpvYk6chxcaGGc5+rNRxP6RLF7OLirJ0MQ1FfZNRdNfUKBvGAF2r6HLp62apGZg0d5gJ5M7zg/1V4ygWW19cuGXyAr5b9bow1MPIOBfrDrMS3CBk2F9DfATTZ/oFwOv/hCUVs2MkWSdr/upJfPt0xoAYtXk1UjxKpV/nHgTTfQy/ijlxEsYn/HY9O6tfqvn/BRvehvPjjoouvxcZteyP1glEcajum7Op67/kV9p4Wv+U+A+31/kyrb3Tjt4GJGpTIBfLi/VjIiHUDFrpAcBz3rFqxyfKQcCqDsOMjD5CrxBj3ow7uMlQiaQoU0Dxwc4eDggbe9Af/F3YqZuvqpj2H0U0hm7+7j56WjFxewVKHLJBwkCXG6a8UdEmKBtA685mUbfpICdYaoLatC4LYOMeqsx6CeEr75vyfdqTdKOZrBKkhTluMbR0s1pvh0kID7B+HL/LKqFSnpgQidfIugk5FUSh42vsBUjrxq6LWYDSHWwYweeuZi/6vVgKQl1XS5X7JUhwNxZSzSF76pNeCIq6KGlCmfIrAnAAhfd0a0xMGy/gOJiqM8JcWjnCMA25UrRL2XrMLC4mnLpi71rATOU62rcgNyczpKI74uHEQAGtQBHfoJjuAqK1JNAK5+j1kIT9gr1F0CC2iJ+68DOI+dveK2lkGqyNYx+eKeTjJSKO4bfzQiM0oBg9eIwqtU3Wn+sSQEg74MzrMjEfaorXh7X+LdO4DwA7dNyEurnYEgmjvjJoNbcxgjhbLSkTe3LFAX7iUia79VIqgD80bxgwoCWyKiSiCKARb/krBWOCOOuF1I6v6azxPHpwsQxC6AvFlOWW7BXsFY9NcCeGvV7l+9ZnLuRjdbjrgdk1b7PRv6aaRzmVk1I3KPBCPvFasP2XhNJXGgYmv7hxiyOci1Eoq4QcXEfr9VsrOEkcl0z4HI8yHx354Lc7Peck8XFvGCe9ElyhxmEtu/ySFQ3BOakjPikOOIA4XlUj3GsUk8qfwnePHVPBnKKm0jtaqjK6tA8uZyKG8jTNgICFZ9wKmsijFMPXVY2lt5zuPnw1V31D//IYjdXpcU1cMfXgZ06vld4anBSEv3WL3zCZaIJsRhxkB7ixumw4h5yieKNJFIVhbV6Ty4Yog9JFmQbAbRfKh1b8ltoym804+hOH8n7oWVqSbtzuylg7DPCIVOcWdUNraXYgmgtXDM6vq0SsDK1j2diz4ywM2BlBSsnCyAdt1NN5gZyMfOufZ3a8SKdwTaRH3bFh6BmfLEZ8NgYbeJQX7GrVbBU5kHT9+k2g3Klchapmqv8mzl0uKvX1av2hW6ce5xo+owa9Qk/Gza4j4o74bVl8tJ/iYClX4kpHY6kek/TOA/Sxj/IvWb1QZvpgmj0LNNCrLdrLUiTDG3lodE9j+Lr8qhmgL8S78= | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData-Original-ChunkCount | string | | 1 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthAs | string | | Internal | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthSource | string | | SJ0PR22MB4941.namprd11.prod.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-FromEntityHeader | string | | Internet | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Id | string | | a417c578-c7ee-480d-a225-d48057e74df5 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-MailboxType | string | | HOSTED | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Network-Message-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-OriginalArrivalTime | string | | 18 Jun 2020 09:11:28.2511 (UTC) | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-UserPrincipalName | string | | bs91VnpEPjrqCnvlIeymwO6ye5P9rggHggVNUPUbV/tC9uuFPVFOYg7e/Cd0MeGmSqT4AlLW0Nn4ZeEqNieSf/D1gp5iLz/YkwjXhYUSJnLRb/csQN4sRMMZsX3LUkKkwVpifaeJzoukLu8qSWn7og== | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthAs | string | | Anonymous | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthMechanism | string | | 04 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthSource | string | | DM6NAM11FT055.eop-nam11.prod.protection.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationInterval | string | | 1:00:00:00.0000000 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationIntervalReason | string | | OriginalSubmit | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTime | string | | 18 Jun 2020 09:11:28.2531 (UTC) | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTimeReason | string | | OriginalSubmit | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-MessageDirectionality | string | | Incoming | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-Network-Message-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-SCL | string | | -1 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Processed-By-BccFoldering | string | | 15.20.3109.017 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersPromoted | string | | DM6NAM11FT064.eop-nam11.prod.protection.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStamped | string | | BN6PR18MB1492 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStripped | string | | DM6NAM11FT064.eop-nam11.prod.protection.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-EndToEndLatency | string | | 00:00:02.7417647 | -action_result.data.\*.internetMessageHeaders.X-MS-Has-Attach | string | | yes | -action_result.data.\*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id-Prvs | string | | 0c3038e5-2c60-453b-188f-08da6ed1ea0c | -action_result.data.\*.internetMessageHeaders.X-MS-Oob-TLC-OOBClassifiers | string | | OLM:1728; | -action_result.data.\*.internetMessageHeaders.X-MS-PublicTrafficType | string | | Email | -action_result.data.\*.internetMessageHeaders.X-MS-TNEF-Correlator | string | | | -action_result.data.\*.internetMessageHeaders.X-MS-TrafficTypeDiagnostic | string | | BN6PR18MB1492: | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam | string | | BCL:0; | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Mailbox-Delivery | string | | wl:1;pcwl:1;ucf:0;jmr:0;auth:0;dest:I;ENG:(750128)(520011016)(520004050)(702028)(944506458)(944626604); | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info | string | | La+CSxAnpzVJOXq7njrFPhIbsh0khleSwldy+W8NYDRsoyyPruPIiId4Avama7JyfzrxoExzhLk5pDn2lGPAJIpdcguiDSsDQg5T+iBCJgFeaEJXjhstECMi842/JGawB9WsiGw9Q/PpvjO5H/2fNLlQZVZW3AAQVZSsX3az4iOsv1Ggj4aYZRMKHmPAtniWOEtQD7zAEWC0jIZf613lWy3vxHfb/3+pV9X8zqPqazbyGy5Q14PICSNkKnvIw8rmeqJV8eSHhvR51Lchib6OIN4xOpLWxkSkBTt5B95RUPnpgPvgp2yLo0Q+EYRIabLDQ0kMsv+24+RnFmr9vo2gRNuFusw8iEPsVEQyhfgIWtBtsBpyvyykxcfa6lIdzQhixZH3Tlkdh1kb15wFS3Ooz3CjaWbY8jcUot5l1p08Ypsj6r7CpIo3xE6jE0x/EeUkDK3Fu/Ol0pOsJ1N5W4iJLdjqSQM3l/t9QWlcPhD8s6D7D7JM5OUHCeFEPr7sSL+P/5zTgBaeUvwtZrlQSH2GHc+5gPW8rkwlwJLJftVEid0gO2PUOrzItzME5PXYAcdx++sF3XC1YMPLet/jMpX8T7/z7+hxFxNyifgmGJ+DkNOec7yGkkcLBz6iCaHx7OrRGwDHIcdAtV85wCk3NEDDiKyHivQpwp/gY55W+wkLe7aqSHmFzm1rUSslx+DWz8w2EgSjJxOmf0JkoNKbTFl3FObkocR0lUUQUnETuoAXUqvpWGD5B69W9XXUM8c43ozz2oBZseheSAtkLil3tMIr/CMCMILPX/LdoErNtkmiFXCPqaLFSSeyO61oCMl6Ezndtwp22nwMPUg5ofG0kdqFuTW122umhy9C6h5BcREaLhWclSyqDoZPB9RvkRlI2kTRwuwbuFW3iOMzmVwxLIQH9K5JkxdMvC3hvNpjVgz7Q2ZnEF3xSNqeoWVQvkaIe8rQLUc8s+HMRUmSERGdfSuQJAx47g8PDs9s3rS/ThUSzIaljJPbUgXEnFg/G6h3I/yXLj2Nj2OG50snoI5jJmE4+69YmNwasdDZuYpnuQeFgu11HtsLniDthJdjEJyYC1utZNt9hgA+6JlLnm7Dxb43cSIiW8ev+3X+b2kREj2k/m8fSz7YgtoCB8AkuiVXRaH3EUiq8XCExbbWeynKRgwCZ6bzvfSiT3+cg+QQKPHFc/cgot56ta6X80tjhFodpTQNTE6V6C9QFHJ3JCVhsSzVifJAc8crI5hAcPbKFEIjinENcfpF/8reo2Yr1xFElhoX | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info-Original | string | | MTrWp0GiNTxQrZWPlJ6veyKekVjJA0FKYcYQlNCKCrjhAtIn4oDZCbWPNm21Zr7mX+CXhspXNSQSEjpIERhcHIWABmuzCF/vnMr0xwLTzuh2T0si6wgTdha+BdCpnx/kvbN+TMK3rCmD9ro6qHf/v1dTbLKJUZ3Cwfl+LoqJLCkw+T8E7/De1QBlCJrKVGfSztXncKPdvPxC2Qjw/QoPmYp7oFbqW8uwNZX1q8HmAXfLzYPeOuLDE/h4s61EeZGvIY7nBMwm6DwKdTqfmQqy3+BTMfoTaN/82aHLgfBI8V0NbiPVvbKbUU8UOusHZM5H6pzOpZFOSsS7yWz1jpIvKK5CJtNUK5XkOCLxv06yzbWib2abzwEhoLUBHYhJGnwNaIznz/C7a6vZLPe1hNxyLBv0/SyYmt1m9v0vB3TSdSH2V+Ork2OK5nz/r1KcOScedjs14GZpifgjEXiIhK4eJlTkI8LE7HnqXr+82C39t13fqV8L8oRYjGiJJE9wpGJIhMa4PQYwgP0D2cQGrVwuVK2qP01TsSE+NWOXyB8hAYn11K/wCnq4TqtwRbptF9aiw0k45eDHfvB/sBARv37sajixtW4QuFQ3eYI9bVVfllmcpnKj5VUD1mdMAnkgpOCiQSEiSI6Tlo9cEycBulSc4fmocQoev7eUTFw43FDjmRpebuNF0lmm7OcvXgS8jBJSzkbLPrrsqTj1fluowoLjQTQsSPn/LK5LdOdCt7x70/BaweDCm9VdcGgh5Mqe0Qp4GxnBSpS61OCmDYZEkCL5W1A+M7Ah9mANYucdydHU9bLxJBcoi42VIDK6oYaKs3oobr0k1M/dk/pjDT65LuHwsA== | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Untrusted | string | | BCL:0; | -action_result.data.\*.internetMessageHeaders.X-Originating-IP | string | | [5.38.181.162] | -action_result.data.\*.internetMessageHeaders.X-Received | string | | by 2002:aa7:84d9:: with SMTP id x25mr2807688pfn.300.1592471487394; Thu, 18 Jun 2020 02:11:27 -0700 (PDT) | -action_result.data.\*.internetMessageHeaders.subject | string | | test html | -action_result.data.\*.internetMessageHeaders.x-ms-exchange-antispam-relay | string | | 0 | -action_result.data.\*.internetMessageHeaders.x-ms-exchange-calendar-series-instance-id | string | | BAAAAIIA4AB0xbcQGoLgCAAAAAAhGy3GuqDYAQAAAAAAAAAAEAAAAMqUNBGdAN9NtAV6x5ZM9JU= | -action_result.data.\*.internetMessageHeaders.x-ms-exchange-senderadcheck | string | | 1 | -action_result.data.\*.internetMessageHeaders.x-ms-traffictypediagnostic | string | | BYAPR18MB2408:EE_MeetingMessage|BL1PR18MB4325:EE_MeetingMessage|DM6NAM11FT064:EE\_|CH0PR11MB5427:EE\_ | -action_result.data.\*.internetMessageId | string | `msgoffice365 internet message id` | | -action_result.data.\*.isAllDay | boolean | | True False | -action_result.data.\*.isDelegated | boolean | | True False | -action_result.data.\*.isDeliveryReceiptRequested | boolean | | True False | -action_result.data.\*.isDraft | boolean | | True False | -action_result.data.\*.isOutOfDate | boolean | | True False | -action_result.data.\*.isRead | boolean | | True False | -action_result.data.\*.isReadReceiptRequested | boolean | | True False | -action_result.data.\*.lastModifiedDateTime | string | | 2017-10-26T01:31:43Z | -action_result.data.\*.meetingMessageType | string | | meetingRequest | -action_result.data.\*.meetingRequestType | string | | newMeetingRequest | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAA | -action_result.data.\*.previousEndDateTime | string | | | -action_result.data.\*.previousEndDateTime.dateTime | string | | 2022-05-09T13:30:00.0000000 | -action_result.data.\*.previousEndDateTime.timeZone | string | | UTC | -action_result.data.\*.previousLocation | string | | | -action_result.data.\*.previousStartDateTime | string | | | -action_result.data.\*.previousStartDateTime.dateTime | string | | 2022-05-09T13:00:00.0000000 | -action_result.data.\*.previousStartDateTime.timeZone | string | | UTC | -action_result.data.\*.receivedDateTime | string | | 2017-10-26T01:31:43Z | -action_result.data.\*.recurrence | string | | | -action_result.data.\*.replyTo | string | | | -action_result.data.\*.responseRequested | boolean | | True False | -action_result.data.\*.sender.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.sender.emailAddress.name | string | `email` | Test Name | -action_result.data.\*.sentDateTime | string | | 2017-10-26T01:31:35Z | -action_result.data.\*.startDateTime.dateTime | string | | 2022-07-26T09:00:00.0000000 | -action_result.data.\*.startDateTime.timeZone | string | | UTC | -action_result.data.\*.subject | string | `msgoffice365 subject` | more attachments | -action_result.data.\*.toRecipients.\*.emailAddress.address | string | `email` | Test@testdomain.abc.com | -action_result.data.\*.toRecipients.\*.emailAddress.name | string | | Test Name | -action_result.data.\*.type | string | | singleInstance | -action_result.data.\*.vaultId | string | | ff89bab9ec1e063a0f100aa7b0ac5fbc7425ab22 | -action_result.data.\*.webLink | string | `url` | https://outlook.office365.com/owa/?ItemID=AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0%2BMMHKonvdoWQcAQSl1b8BFiEmbqZql%2BJiUtwAAAgEMAAAAQSl1b8BFiEmbqZql%2BJiUtwABS2DpfAAAAA%3D%3D&exvsurl=1&viewmodel=ReadMessageItem | -action_result.summary | string | | | -action_result.message | string | | Successfully fetched email | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.filter | string | | | +action_result.parameter.limit | numeric | | | +action_result.data.\*.id | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.givenName | string | | | +action_result.data.\*.surname | string | | | +action_result.data.\*.userPrincipalName | string | | | +action_result.data.\*.mail | string | | | +action_result.data.\*.jobTitle | string | | | +action_result.data.\*.mobilePhone | string | | | +action_result.data.\*.officeLocation | string | | | +action_result.data.\*.businessPhones.\* | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'get email properties' +## action: 'move email' -Get non-standard email properties from the server +Move an email to a folder -Type: **investigate**
+Type: **generic**
Read only: **True** -For a list of possible properties to retrieve, visit https://docs.microsoft.com/en-us/graph/api/message-get?view=graph-rest-1.0&tabs=http. - #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**id** | required | Message ID to get properties of | string | `msgoffice365 message id` | -**email_address** | required | Email address of the mailbox owner | string | `email` | -**get_headers** | optional | Get email headers | boolean | | -**get_body** | optional | Get email body | boolean | | -**get_unique_body** | optional | Get unique email body | boolean | | -**get_sender** | optional | Get email sender | boolean | | -**properties_list** | optional | Other properties to get (comma-separated list) | string | | +**id** | required | Message ID to move | string | `msgoffice365 message id` | +**email_address** | required | User's email (mailbox to move from) | string | `email` | +**folder** | required | Destination folder name/path or ID | string | `msgoffice365 folder id` | +**get_folder_id** | optional | Retrieve folder ID from folder name/path | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.email_address | string | `email` | user@abc.com | -action_result.parameter.get_body | boolean | | True False | -action_result.parameter.get_headers | boolean | | True False | -action_result.parameter.get_sender | boolean | | True False | -action_result.parameter.get_unique_body | boolean | | True False | -action_result.parameter.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA== | -action_result.parameter.properties_list | string | | subject,receivedDateTime | -action_result.data.\*.@odata.context | string | `url` | https://test.abc.com/v1.0/$metadata#users('user%40.abc.com')/messages(internetMessageHeaders,body,uniqueBody,sender,subject)/$entity | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAO8DBJl" | -action_result.data.\*.body.content | string | | `\\r\\n

HTML heading

HTML body.` | -action_result.data.\*.body.contentType | string | | html | -action_result.data.\*.id | string | | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA== | -action_result.data.\*.internetMessageHeaders.Accept-Language | string | | en-US | -action_result.data.\*.internetMessageHeaders.Authentication-Results | string | | spf=pass (sender IP is 209.85.210.171) smtp.mailfrom=testdomain.com; .abc.com; dkim=pass (signature was verified) header.d=testdomain.com.20150623.gappssmtp.com;.abc.com; dmarc=pass action=none header.from=testdomain.com;compauth=pass reason=100 | -action_result.data.\*.internetMessageHeaders.Content-Language | string | | en-US | -action_result.data.\*.internetMessageHeaders.Content-Transfer-Encoding | string | | binary | -action_result.data.\*.internetMessageHeaders.Content-Type | string | | multipart/related | -action_result.data.\*.internetMessageHeaders.DKIM-Signature | string | | v=1; a=rsa-sha256; c=relaxed/relaxed; d=testdomain.com.20150623.gappssmtp.com; s=20150623; h=message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=avAAeJ8jF08K4oIBhxTirRmyB+SXHwdU0zdxv7eqs/zWaWWcgmT0007KP560TTgo5u oD4nb6TvKxpRyWW4QwmkbuMIwHsMvehd2l1gispV3AawyGJjpmN7ErVYfLtIkz2Tap3V YxmluV+SqeyyxTU8pFAEZ7+2C2lOb1DO5TC7xCMv+dyzevSscJdbeN0dFkG+C93zCqkg w2fxubx2HDD7b/U6m2wXllYhH608wKJ/qYzyvQyqxYqNiQOtPRg2gw4sZ2UgN3+UQyVq 8ubO39ZuqakJpzEzYMw10d6E7SQhvHDJH7mFwhBlzhvOpb2gLJDN8n8dJaZo05BozQqq MsvA== | -action_result.data.\*.internetMessageHeaders.Date | string | | Thu, 18 Jun 2020 02:11:26 -0700 | -action_result.data.\*.internetMessageHeaders.From | string | | "Test" | -action_result.data.\*.internetMessageHeaders.In-Reply-To | string | | | -action_result.data.\*.internetMessageHeaders.MIME-Version | string | | 1.0 | -action_result.data.\*.internetMessageHeaders.Message-ID | string | | <5eeb2fbe.1c69fb81.22b4b.676a@mx.test.com> | -action_result.data.\*.internetMessageHeaders.Received | string | | from localhost.localdomain (host-240.test.com. [204.107.141.240]) by tset.abc.com with UTF8SMTPSA id ng12sm1923252pjb.15.2020.06.18.02.11.26 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jun 2020 02:11:26 -0700 (PDT) | -action_result.data.\*.internetMessageHeaders.Received-SPF | string | | Pass (protection.test.com: domain of testdomain.com designates 209.85.210.171 as permitted sender) receiver=protection.test.com; client-ip=209.85.210.171; helo=mail-pf1-f171.test.com; | -action_result.data.\*.internetMessageHeaders.References | string | | | -action_result.data.\*.internetMessageHeaders.Return-Path | string | `email` | notifications@testdomain.com | -action_result.data.\*.internetMessageHeaders.Subject | string | | Fw: Email having different attachments | -action_result.data.\*.internetMessageHeaders.Thread-Index | string | | AQHWZLqyXR4k4Sc6skyFCMPITcMsbKpGS7Bm | -action_result.data.\*.internetMessageHeaders.Thread-Topic | string | | Email having different attachments | -action_result.data.\*.internetMessageHeaders.To | string | | "Test" | -action_result.data.\*.internetMessageHeaders.X-EOPAttributedMessage | string | | 0 | -action_result.data.\*.internetMessageHeaders.X-EOPTenantAttributedMessage | string | | a417c578-c7ee-480d-a225-d48057e74df5:0 | -action_result.data.\*.internetMessageHeaders.X-Forefront-Antispam-Report | string | | CIP:209.85.210.171;CTRY:US;LANG:en;SCL:-1;SRV:;IPV:NLI;SFV:SFE;H:mail-pf1-f171.test.com;PTR:mail-pf1-f171.test.com;CAT:NONE;SFTY:;SFS:;DIR:INB;SFP:; | -action_result.data.\*.internetMessageHeaders.X-Gm-Message-State | string | | AOAM533ynFERIhSIewEEkj4b8B1rPNOEeie1IxBdrd55treEMtBa1jkL cO5ee4Ff6p0FYedfFtVtHKiCglGTpFTOSw== | -action_result.data.\*.internetMessageHeaders.X-Google-DKIM-Signature | string | | v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=fPT47NIiheeY6GM0bxUOlsmnOgN4WuiOlalFvZqrAiFiOoYk6zrznvgIcAtiHZ4nxE naQAa+mZs5svqRjib3YI52OvR5U8MitIYaa0Rt3LyYSUO1s3iKTUs4nHyRnqPt1skNl7 2OUwsZPXo3ShJDw/uxZRu/cuN1iIfeuE02PrbR04p4D8+1XRslqt/Xqm/bOWKUauqZWe dH1E7meFY01hXxODreO4nWHIhsZgr49TpP/OqRyFcyKHHFFg2sPGXz+QNah6jP4YQUYd Tty2wzOX3nc/YS7TkVo3ORmbzh9o+UZaqH8wHbQlyTdklYxoMPvJwZTo72rTxZeqiJ9E J7PQ== | -action_result.data.\*.internetMessageHeaders.X-Google-Smtp-Source | string | | ABdhPJxrYC7raBubCCIOmauxmxryzS9KsihTN6XCRgaNp2rDrG71TVxryzYCtelFOZ2Xj1LzcYIiMA== | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData | string | | VSM9HTzub/OH3NCwKXEQqkkzjnhdw5kXsgd9WM0SRgZ0qRdPg5D9/o3LA7lf8ziXc5k0mm9M5mHvFoYePXNXs/MGhGdBGxa/qUQ+FVHA2mDgfPkamJCEZxz//OX/uruTDo+zF4p9D1dQJpnIpx1M75OhuvrHX/BxWWzyAh78DXfF214YHdyFBCYepwl56CS7+fSGQL/r3p+OvWIBnIkISC+HJljSro2k47pPPAkspMhoUkb+zklyENFjez+JcEHYlih2FiNeUO8kb9b7qvlm3zPK98HLspzDh4BojpQ6Ff330iy7nfIK726tCMByxjOdnEQSB9Ua2sbE5gxSeeWL8MB5DHcQSSsXg+sR8w4gXrXLO3meE0lNQKRoAv2b1U0Q+yM0QBqeQWlymZG21bKeuH4gtAFQvfXNjoCtIbBQK1n7ZnL7fI21FJZRcMcKEneus6gLYUqD4PdLEq9FEGbfgiLmVYeUAL2A0Q/gectvL1OVudtHVR5gFMJKt65F1OtS04CPulfLLFSl1F4AzpjjtBSyQcK9R7bOsjoHxQXPMd9fMCzMSIq5f551pO0klKqWY7l11Un2Noj6CA7EtXiD1bTv8JmYQEKR+0HTZagNd+79GeTvKjxTvt9MkyO8k3aqWyNqT331ITnVICtksN1TVMCp8GVeDudNMr2PLSW0alOduR5unuEgTWrqHoaTGOovQx0PVjudNlpZ80ANK9hqaC/ZhLLOtNpJ3fZnjs06PzrPLGhE/IeccY1n8sYDvGm1QA9TN6JaaGPl1Pj6ecy16k0XuF/PKGHTL0M4LCpxSS6T87oFFH1zHkKtmbJp3aAI4bt3ihbQmwFb29JyMgL7ZOy+zrIwXGILh1KQGWQQv1uXXnAuqQy29HeFXs6D2hDHxHlBk5ZQ+vgRtsvRvGnq58vJ3CapjntfL3pOINUj1avLyAZxjasBWMTwaZs9JQ4ZIMekzkIk05lh9XfDSeULk2yKaH8YSCC6ENUHxSWa6pPHJfOdp9kXwOtlp09/VTTAikKy862k9ybN4bRWZB45B9Pv5scna8IX3rthIXUih8c= | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthAs | string | | Internal | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthSource | string | | SJ0QA11MB4941.namprd11.prod.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-FromEntityHeader | string | | Internet | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Id | string | | a417c578-c7ee-480d-a225-d48057e74df5 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-MailboxType | string | | HOSTED | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Network-Message-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-OriginalArrivalTime | string | | 18 Jun 2020 09:11:28.2511 (UTC) | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-UserPrincipalName | string | | bs91VnpEPjrqCnvlIeymwO6ye4Q8rggHggVNUPUbV/tC9uuFPVFOYg7e/Cd0MeGmSqT4AlLW0Nn4ZeEqNieSf/D1gp5iLz/YkwjXhYUSJnLRb/csQN4sRMMZsX3LUkKkwVpifaeJzoukLu8qSWn7og== | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthAs | string | | Anonymous | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthMechanism | string | | 04 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthSource | string | | DM6NAM11FT055.eop-nam11.prod.protection.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationInterval | string | | 1:00:00:00.0000000 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationIntervalReason | string | | OriginalSubmit | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTime | string | | 18 Jun 2020 09:11:28.2531 (UTC) | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTimeReason | string | | OriginalSubmit | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-MessageDirectionality | string | | Incoming | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-Network-Message-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-SCL | string | | -1 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Processed-By-BccFoldering | string | | 15.20.3109.017 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStamped | string | | BN6PR18MB1492 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-EndToEndLatency | string | | 00:00:02.7417647 | -action_result.data.\*.internetMessageHeaders.X-MS-Has-Attach | string | | yes | -action_result.data.\*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Oob-TLC-OOBClassifiers | string | | OLM:1728; | -action_result.data.\*.internetMessageHeaders.X-MS-PublicTrafficType | string | | Email | -action_result.data.\*.internetMessageHeaders.X-MS-TNEF-Correlator | string | | | -action_result.data.\*.internetMessageHeaders.X-MS-TrafficTypeDiagnostic | string | | BN6PR18MB1492: | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam | string | | BCL:0; | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Mailbox-Delivery | string | | wl:1;pcwl:1;ucf:0;jmr:0;auth:0;dest:I;ENG:(750128)(520011016)(520004050)(702028)(944506458)(944626604); | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info | string | | La+CSxAnpzVJOXq7njrFPhIbsh0khleSwldy+W8NYDRsoyyPruPIiId4Avama7JyfzrxoExzhLk5pDn2lGPAJIpdcguiDSsDQg5T+iBCJgFeaEJXjhstECMi842/JGawB9WsiGw9Q/PpvjO5H/2fNLlQZVZW3AAQVZSsX3az4iOsv1Ggj4aYZRMKHmPAtniWOEtQD7zAEWC0jIZf613lWy3vxHfb/3+pV9X8zqPqazbyGy5Q14PICSNkKnvIw8rmeqJV8eSHhvR51Lchib6OIN4xOpLWxkSkBTt5B95RUPnpgPvgp2yLo0Q+EYRIabLDQ0kMsv+24+RnFmr9vo2gRNuFusw8iEPsVEQyhfgIWtBtsBpyvyykxcfa6lIdzQhixZH3Tlkdh1kb15wFS3Ooz3CjaWbY8jcUot5l1p08Ypsj6r7CpIo3xE6jE0x/EeUkDK3Fu/Ol0pOsJ1N5W4iJLdjqSQM3l/t9QWlcPhD8s6D7D7JM5OUHCeFEPr7sSL+P/5zTgBaeUvwtZrlQSH2GHc+5gPW8rkwlwJLJftVEid0gO2PUOrzItzME5PXYAcdx++sF3XC1YMPLet/jMpX8T7/z7+hxFxNyifgmGJ+DkNOec7yGkkcLBz6iCaHx7OrRGwDHIcdAtV85wCk3NEDDiKyHivQpwp/gY55W+wkLe7aqSHmFzm1rUSslx+DWz8w2EgSjJxOmf0JkoNKbTFl3FObkocR0lUUQUnETuoAXUqvpWGD5B69W9XXUM8c43ozz2oBZseheSAtkLil3tMIr/CMCMILPX/LdoErNtkmiFXCPqaLFSSeyO61oCMl6Ezndtwp22nwMPUg5ofG0kdqFuTW122umhy9C6h5BcREaLhWclSyqDoZPB9RvkRlI2kTRwuwbuFW3iOMzmVwxLIQH9K5JkxdMvC3hvNpjVgz7Q2ZnEF3xSNqeoWVQvkaIe8rQLUc8s+HMRUmSERGdfSuQJAx47g8PDs9s3rS/ThUSzIaljJPbUgXEnFg/G6h3I/yXLj2Nj2OG50snoI5jJmE4+69YmNwasdDZuYpnuQeFgu11HtsLniDthJdjEJyYC1utZNt9hgA+6JlLnm7Dxb43cSIiW8ev+3X+b2kREj2k/m8fSz7YgtoCB8AkuiVXRaH3EUiq8XCExbbWeynKRgwCZ6bzvfSiT3+cg+QQKPHFc/cgot56ta6X80tjhFodpTQNTE6V6C9QFHJ3JCVhsSzVifJAc8crI5hAcPbKFEIjinENcfpF/8reo2Yr1xFElhoX | -action_result.data.\*.internetMessageHeaders.X-Originating-IP | string | | [2.39.180.162] | -action_result.data.\*.internetMessageHeaders.X-Received | string | | by 2002:aa7:84d9:: with SMTP id x25mr2807688pfn.300.1592471487394; Thu, 18 Jun 2020 02:11:27 -0700 (PDT) | -action_result.data.\*.internetMessageHeaders.subject | string | | test html | -action_result.data.\*.receivedDateTime | string | | 2020-06-18T09:11:31Z | -action_result.data.\*.sender.emailAddress.address | string | `email` | notifications@testdomain.com | -action_result.data.\*.sender.emailAddress.name | string | `email` | notifications@testdomain.com | -action_result.data.\*.subject | string | | test html | -action_result.data.\*.uniqueBody.content | string | |
\\r\\n
\\r\\n

HTML heading

\\r\\nHTML body.
\\r\\n
\\r\\n | -action_result.data.\*.uniqueBody.contentType | string | | html | -action_result.summary | string | | | -action_result.message | string | | Successfully fetched email | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.id | string | `msgoffice365 message id` | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.folder | string | `msgoffice365 folder id` | | +action_result.parameter.get_folder_id | boolean | | | +action_result.data.\*.id | string | | | +action_result.data.\*.subject | string | | | +action_result.data.\*.parentFolderId | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'run query' +## action: 'oof check' -Search emails +Get user's out of office status Type: **investigate**
Read only: **True** -If the query or internet_message_id parameters are included, the subject, sender, body, and range parameters will be ignored. The internet_message_id parameter will take precedence over the query parameter.

For details on formatting the query parameter, refer to Microsoft Graph Query Parameters. Query parameters can include OData system query options or other supported parameters.

If the limit parameter is not included, the action will default to limiting to ten emails that match the rest of the query. The get_folder_id parameter should be enabled only when you specified folder name/folder path in the folder parameter. If you provide folder ID in the folder parameter and set get_folder_id parameter to true, it will throw an error of folder ID not found for given folder name (because the action considers folder parameter value as folder name/folder path). The folder parameter must be either a (case sensitive) well-known name [list here; https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0] or the internal o365 folder ID. The action supports searching for a folder that is nested within another. To copy in such a folder, specify the complete folder path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate.
When the search_well_known_folders parameter is set to true, action will ignore values provided in the folder and get_folder_id parameters and the user will get details from all 17 well-known folders which are listed below:
  • Archive
  • Clutter
  • Conflicts
  • Conversation History
  • Deleted Items
  • Drafts
  • Inbox
  • Junk Email
  • Local Failures
  • Msg Folder Root
  • Outbox
  • Recoverable Items Deletions
  • Scheduled
  • Search Folders
  • Sent Items
  • Server Failures
  • Sync Issues

If the limit parameter is provided, the user will get the number of messages provided in the limit from every folder if present. - #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**email_address** | required | User's email (mailbox to search in) | string | `email` | -**folder** | optional | Destination folder; this must be either a (case-sensitive) well-known name or the internal o365 folder ID | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` `msgoffice365 folder id` | -**search_well_known_folders** | optional | Checks all well known folders for messages, ignores folder name provided in parameter | boolean | | -**get_folder_id** | optional | Assume the folder parameter contains a folder name/folder path, separated by '/'(forward slash) ; i.e. Inbox/dir1/dir2/dir3. If this parameter is enabled, it retrieves the folder ID for the provided folder name/folder path automatically and replaces the parameter value | boolean | | -**subject** | optional | Substring to search in subject | string | `msgoffice365 subject` | -**body** | optional | Substring to search in body | string | | -**sender** | optional | Sender email address to match | string | `email` | -**limit** | optional | Maximum emails to return | numeric | | -**query** | optional | MS Graph query string | string | | -**internet_message_id** | optional | Internet message ID | string | `msgoffice365 internet message id` | +**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.body | string | | How are you doing this fine evening? | -action_result.parameter.email_address | string | `email` | test@testdomain.abc.com | -action_result.parameter.folder | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` `msgoffice365 folder id` | Archive | -action_result.parameter.get_folder_id | boolean | | True False | -action_result.parameter.internet_message_id | string | `msgoffice365 internet message id` | | -action_result.parameter.limit | numeric | | 5 | -action_result.parameter.query | string | | $filter=contains(subject,'Urgent') | -action_result.parameter.search_well_known_folders | boolean | | True False | -action_result.parameter.sender | string | `email` | test@testdomain.abc.com | -action_result.parameter.subject | string | `msgoffice365 subject` | Just wanted to say hello | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFOpxtE" | -action_result.data.\*.@odata.type | string | | #test.abc.eventMessageRequests | -action_result.data.\*.allowNewTimeProposals | string | | | -action_result.data.\*.bccRecipients.\*.emailAddress.address | string | | test3.test@test.com | -action_result.data.\*.bccRecipients.\*.emailAddress.name | string | | test3.test@test.com | -action_result.data.\*.bccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.bccRecipients.name | string | | Test Name | -action_result.data.\*.body.content | string | | `\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n
\\r\\nTest
\\r\\n
\\r\\n\\r\\n\\r\\n` | -action_result.data.\*.body.contentType | string | | text | -action_result.data.\*.bodyPreview | string | | How are you doing this fine evening? | -action_result.data.\*.categories | string | | | -action_result.data.\*.ccRecipients.\*.emailAddress.address | string | | test3.test@test.com | -action_result.data.\*.ccRecipients.\*.emailAddress.name | string | | test3.test@test.com | -action_result.data.\*.ccRecipients.email | string | `email` | test@testdomain.abc.com | -action_result.data.\*.ccRecipients.name | string | | Test Name | -action_result.data.\*.changeKey | string | | CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFOpxtE | -action_result.data.\*.conversationId | string | | AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQAGqbDRkVLxZMtetM-dKqAPo= | -action_result.data.\*.conversationIndex | string | | AQHXHRZ01/QE6F/kQkdaSwXyspIYQagZQ== | -action_result.data.\*.createdDateTime | string | | 2017-10-30T22:32:42Z | -action_result.data.\*.endDateTime.dateTime | string | | 2020-08-15T12:30:00.0000000 | -action_result.data.\*.endDateTime.timeZone | string | | UTC | -action_result.data.\*.flag.flagStatus | string | | notFlagged | -action_result.data.\*.from.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.from.emailAddress.name | string | | Test Name | -action_result.data.\*.hasAttachments | boolean | | True False | -action_result.data.\*.id | string | `msgoffice365 message id` | AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAAAAADJbdfk-sdvT4wwcqie92hZBwBBKXVvwEWISZupmqX4mJS3AACEV3zJAABBKXVvwEWISZupmqX4mJS3AAFOZwS4AAA= | -action_result.data.\*.importance | string | | normal | -action_result.data.\*.inferenceClassification | string | | focused | -action_result.data.\*.internetMessageId | string | `msgoffice365 internet message id` | | -action_result.data.\*.isAllDay | boolean | | True False | -action_result.data.\*.isDelegated | boolean | | True False | -action_result.data.\*.isDeliveryReceiptRequested | boolean | | True False | -action_result.data.\*.isDraft | boolean | | True False | -action_result.data.\*.isOutOfDate | boolean | | True False | -action_result.data.\*.isRead | boolean | | True False | -action_result.data.\*.isReadReceiptRequested | boolean | | True False | -action_result.data.\*.lastModifiedDateTime | string | | 2017-10-30T22:32:53Z | -action_result.data.\*.meetingMessageType | string | | meetingRequest | -action_result.data.\*.meetingRequestType | string | | informationalUpdate | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAAAAADJbdfk-sdvT4wwcqie92hZAQBBKXVvwEWISZupmqX4mJS3AACEV3zJAAA= | -action_result.data.\*.previousEndDateTime | string | | | -action_result.data.\*.previousEndDateTime.dateTime | string | | 2020-08-15T12:30:00.0000000 | -action_result.data.\*.previousEndDateTime.timeZone | string | | UTC | -action_result.data.\*.previousLocation | string | | | -action_result.data.\*.previousStartDateTime | string | | | -action_result.data.\*.previousStartDateTime.dateTime | string | | 2020-08-15T12:00:00.0000000 | -action_result.data.\*.previousStartDateTime.timeZone | string | | UTC | -action_result.data.\*.receivedDateTime | string | | 2017-10-30T22:32:42Z | -action_result.data.\*.recurrence | string | | | -action_result.data.\*.replyTo | string | | | -action_result.data.\*.replyTo.\*.emailAddress.address | string | | hellohi@test.com | -action_result.data.\*.replyTo.\*.emailAddress.name | string | | hellohi@test.com | -action_result.data.\*.responseRequested | boolean | | True False | -action_result.data.\*.sender.emailAddress.address | string | `email` | test@testdomain.abc.com | -action_result.data.\*.sender.emailAddress.name | string | | Test Name | -action_result.data.\*.sentDateTime | string | | 2017-10-30T22:32:37Z | -action_result.data.\*.startDateTime.dateTime | string | | 2020-08-15T12:00:00.0000000 | -action_result.data.\*.startDateTime.timeZone | string | | UTC | -action_result.data.\*.subject | string | `msgoffice365 subject` | Just wanted to say hello | -action_result.data.\*.toRecipients.\*.emailAddress.address | string | `email` | Test@testdomain.abc.com | -action_result.data.\*.toRecipients.\*.emailAddress.name | string | | Test Name | -action_result.data.\*.type | string | | singleInstance | -action_result.data.\*.vaultId | string | `sha1` `vault id` | 719dbf72d7c0bc89d7e34306c08a0b66191902b9 | -action_result.data.\*.webLink | string | `url` | https://outlook.office365.com/owa/?ItemID=AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAAAAADJbdfk%2FsdvT4wwcqie92hZBwBBKXVvwEWISZupmqX4mJS3AACEV3zJAABBKXVvwEWISZupmqX4mJS3AAFOZwS4AAA%3D&exvsurl=1&viewmodel=ReadMessageItem | -action_result.summary.emails_matched | numeric | | 1 | -action_result.message | string | | Emails matched: 1 | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.data.\*.status | string | | | +action_result.data.\*.externalAudience | string | | | +action_result.data.\*.externalReplyMessage | string | | | +action_result.data.\*.internalReplyMessage | string | | | +action_result.data.\*.scheduledStartDateTime | string | | | +action_result.data.\*.scheduledEndDateTime | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'create folder' - -Create a new folder +## action: 'report message' -Type: **generic**
-Read only: **False** +Add the sender email into the report -Create a new folder either in the mailbox root or inside an existing folder. The action supports creating a folder that is nested within another. To create in such a folder, specify the complete path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate. +Type: **contain**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**email_address** | required | User's email (mailbox to create folders) | string | `email` | -**folder** | required | Folder Name/Path. Use '/'to separate folder elements; i.e. Inbox/dir1/dir2/dir3 | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` | -**all_subdirs** | optional | Make any missing directories in the path if they don't exist instead of failing | boolean | | +**message_id** | required | Message ID to pick the sender of | string | `msgoffice365 message id` | +**user_id** | required | User ID to base the action of | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**is_message_move_requested** | optional | Indicates whether the message should be moved out of current folder | boolean | | +**report_action** | required | Indicates the type of action to be reported on the message | string | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.all_subdirs | boolean | | True False | -action_result.parameter.email_address | string | `email` | test@testdomain.abc.com | -action_result.parameter.folder | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` | Archive | -action_result.data.\*.@odata.context | string | `url` | https://test.abc.com/v1.0/$metadata#users('abc%def.test.com')/mailFolders/$entity | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFOpxtE" | -action_result.data.\*.childFolderCount | numeric | | 1 | -action_result.data.\*.displayName | string | | | -action_result.data.\*.id | string | `msgoffice365 folder id` | AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAAAAADJbdfk-sdvT4wwcqie92hZBwBBKXVvwEWISZupmqX4mJS3AACEV3zJAABBKXVvwEWISZupmqX4mJS3AAFOZwS4AAA= | -action_result.data.\*.isHidden | boolean | | True False | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAAAAADJbdfk-sdvT4wwcqie92hZAQBBKXVvwEWISZupmqX4mJS3AACEV3zJAAA= | -action_result.data.\*.sizeInBytes | numeric | | 0 | -action_result.data.\*.totalItemCount | numeric | | 1 | -action_result.data.\*.unreadItemCount | numeric | | 1 | -action_result.summary.folder | string | | AQMkAMExNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAFA6de0wAAAA== | -action_result.summary.folders created | numeric | | 1 | -action_result.message | string | | Emails matched: 1 | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.message_id | string | `msgoffice365 message id` | | +action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.parameter.is_message_move_requested | boolean | | | +action_result.parameter.report_action | string | | | +action_result.data.\*.message | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'get folder id' +## action: 'send email' -Get the API ID of the folder +Send an email -Type: **investigate**
+Type: **generic**
Read only: **True** -The action supports searching a folder that is nested within another. To search in such a folder, specify the complete path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate. - #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**email_address** | required | User's email (mailbox) | string | `email` | -**folder** | required | Folder Name/Path. Use '/' to separate folder elements; i.e. Inbox/dir1/dir2/dir3 | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` | +**from_email** | required | From email address | string | `email` | +**to** | required | To email addresses (comma-separated) | string | `email` | +**cc** | optional | CC email addresses (comma-separated) | string | | +**bcc** | optional | BCC email addresses (comma-separated) | string | | +**subject** | required | Email subject | string | | +**body** | required | Email body | string | | +**body_is_html** | optional | Is body HTML | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.email_address | string | `email` | test@testdomain.abc.com | -action_result.parameter.folder | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` | Test/Testy/subfolders | -action_result.data.\*.folder | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` | Test | -action_result.data.\*.folder_id | string | `msgoffice365 folder id` | AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAEApxCRAAA= | -action_result.data.\*.path | string | `msgoffice365 mail folder` `msgoffice365 mail folder path` | | -action_result.summary.folder_id | string | `msgoffice365 folder id` | AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAEApxCTAAA= | -action_result.message | string | | Folder id: AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAEApxCTAAA= | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.from_email | string | `email` | | +action_result.parameter.to | string | `email` | | +action_result.parameter.cc | string | | | +action_result.parameter.bcc | string | | | +action_result.parameter.subject | string | | | +action_result.parameter.body | string | | | +action_result.parameter.body_is_html | boolean | | | +action_result.data.\*.message | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'send email' - -Sends an email with optional text rendering. Attachments are allowed a Content-ID tag for reference within the html +## action: 'unblock sender' -Type: **generic**
-Read only: **False** +Remove a sender from the blocked senders list -
Notes
  • If the from parameter is not provided, then the action will consider the username parameter provided in the asset configuration as the sender's email address.
  • The send email action is executed in two stages. Before sending an email it creates a draft of the email. Once the draft is successfully saved, the email is sent.
+Type: **correct**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**from** | optional | From field | string | `email` | -**to** | required | List of recipients email addresses | string | `email` | -**cc** | optional | List of recipients email addresses to include on cc line | string | `email` | -**bcc** | optional | List of recipients email addresses to include on bcc line | string | `email` | -**subject** | required | Message Subject | string | | -**headers** | optional | Serialized json dictionary. Additional email headers to be added to the message | string | | -**body** | required | Html rendering of message | string | | -**attachments** | optional | List of vault ids of files to attach to the email. Vault id is used as content id | string | `sha1` `vault id` | +**email_address** | required | User's email address (mailbox) | string | `email` | +**sender** | required | Email address of sender to unblock | string | `email` | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.attachments | string | `sha1` `vault id` | da39a3ee5e6b4b0d3255bfef95601890afd80709 | -action_result.parameter.bcc | string | `email` | test@testdomain.abc.com | -action_result.parameter.body | string | |

Have a good time with these.

| -action_result.parameter.cc | string | `email` | test@testdomain.abc.com | -action_result.parameter.from | string | `email` | test@testdomain.abc.com | -action_result.parameter.headers | string | | {"x-custom-header":"Custom value"} | -action_result.parameter.subject | string | | Example subject | -action_result.parameter.to | string | `email` | test@testdomain.abc.com | -action_result.data.\*.@odata.context | string | `url` | https://test.abc.com/v1.0/$metadata#users('user%40.abc.com')/messages(internetMessageHeaders,body,uniqueBody,sender,subject)/$entity | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAO8DBJl" | -action_result.data.\*.body.content | string | | `Have a good time with these.\\r\\n` | -action_result.data.\*.body.contentType | string | | html | -action_result.data.\*.bodyPreview | string | | Have a good time with these. | -action_result.data.\*.changeKey | string | | CQAAABYAAADTteE6Q2eCQKSqg19j6T+NAAYzSv5R | -action_result.data.\*.conversationId | string | | AAQkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAQAORC3aOpHnZMsHD4-7L40sY= | -action_result.data.\*.conversationIndex | string | | AQHZopYz5ELdo6kedkywcPj/svjSxg== | -action_result.data.\*.createdDateTime | string | | 2023-06-19T10:09:58Z | -action_result.data.\*.flag.flagStatus | string | | notFlagged | -action_result.data.\*.from.emailAddress.address | string | `email` | test@test.com | -action_result.data.\*.from.emailAddress.name | string | | Ryan Edwards | -action_result.data.\*.hasAttachments | boolean | | True False | -action_result.data.\*.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA== | -action_result.data.\*.importance | string | | normal | -action_result.data.\*.inferenceClassification | string | | focused | -action_result.data.\*.internetMessageId | string | `msgoffice365 internet message id` | | -action_result.data.\*.isDeliveryReceiptRequested | boolean | | True False | -action_result.data.\*.isDraft | boolean | | True False | -action_result.data.\*.isRead | boolean | | True False | -action_result.data.\*.isReadReceiptRequested | boolean | | True False | -action_result.data.\*.lastModifiedDateTime | string | | 2023-06-19T10:09:58Z | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAAAgEPAAAA | -action_result.data.\*.receivedDateTime | string | | 2020-06-18T09:11:31Z | -action_result.data.\*.sender.emailAddress.address | string | `email` | notifications@testdomain.com | -action_result.data.\*.sender.emailAddress.name | string | `email` | notifications@testdomain.com | -action_result.data.\*.sentDateTime | string | | 2023-06-19T10:09:58Z | -action_result.data.\*.subject | string | | test html | -action_result.data.\*.toRecipients.\*.emailAddress.address | string | `email` | test@test.com | -action_result.data.\*.toRecipients.\*.emailAddress.name | string | | Ryan Edwards | -action_result.data.\*.webLink | string | | https://outlook.office365.com/owa/?ItemID=AAkALgAAAAAAHYQDEapmEc2byACqAC%2FEWg0A07XhOkNngkCkqoNfY%2Bk%2FjQAGNNQOowAA&exvsurl=1&viewmodel=ReadMessageItem | -action_result.summary | string | | | -action_result.message | string | | Successfully sent email | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.sender | string | `email` | | +action_result.data.\*.message | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'on poll' +## action: 'update email' -Ingest emails from Office 365 using Graph API +Update properties of an email -Type: **ingest**
+Type: **generic**
Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**start_time** | optional | Parameter Ignored in this app | numeric | | -**end_time** | optional | Parameter Ignored in this app | numeric | | -**container_id** | optional | Parameter Ignored in this app | string | | -**container_count** | required | Maximum number of emails to ingest | numeric | | -**artifact_count** | optional | Parameter Ignored in this app | numeric | | +**id** | required | Message ID to update | string | `msgoffice365 message id` | +**email_address** | required | User's email address (mailbox) | string | `email` | +**category** | optional | Category to add to the email | string | | +**is_read** | optional | Mark email as read | boolean | | #### Action Output -No Output - -## action: 'update email' +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.id | string | `msgoffice365 message id` | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.category | string | | | +action_result.parameter.is_read | boolean | | | +action_result.data.\*.id | string | | | +action_result.data.\*.subject | string | | | +action_result.data.\*.isRead | boolean | | True False | +action_result.data.\*.categories | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | -Update an email on the server +## action: 'get email' -Type: **generic**
-Read only: **False** +Get an email from the server -Currently, this action only updates the categories and subject of an email. To set multiple categories, please pass a comma-separated list to the category parameter.
NOTE: If the user tries to update the categories, then the existing categories of the email will be replaced with the new categories provided as input. +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**id** | required | Message ID to delete | string | `msgoffice365 message id` | -**email_address** | required | Email address of the mailbox owner | string | `email` | -**subject** | optional | Subject to set | string | | -**categories** | optional | Categories to set | string | | +**id** | required | Message ID to get | string | `msgoffice365 message id` | +**email_address** | required | User's email address (mailbox) | string | `email` | +**get_headers** | optional | Get email headers | boolean | | +**download_attachments** | optional | Download attachments | boolean | | +**download_email** | optional | Download email as EML file | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.categories | string | | Yellow, Blue, Purple, red | -action_result.parameter.email_address | string | `email` | test@sample.com | -action_result.parameter.id | string | `msgoffice365 message id` | AAMkAGIyMTUxYTkzLWRjYjctNDFjMi04NTAxLTQzMDFkNDhlZmI5MQBGAAAAAACxQSnX8n2GS4cunBIQ2sV7BwCQhMsoV7EYSJF42ChR9SCxAAAAYCbsAACQhMsoV7EYSJF42ChR9SCxAAAAjh8bAAA= | -action_result.parameter.subject | string | | Both value are modified | -action_result.data.\*.@odata.context | string | `url` | https://test.abc.com/v1.0/$metadata#users('user%40.abc.com')/messages(internetMessageHeaders,body,uniqueBody,sender,subject)/$entity | -action_result.data.\*.@odata.etag | string | | W/"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAO8DBJl" | -action_result.data.\*.body.content | string | | `Have a good time with these.\\r\\n` | -action_result.data.\*.body.contentType | string | | html | -action_result.data.\*.bodyPreview | string | | Have a good time with these. | -action_result.data.\*.changeKey | string | | CQAAABYAAADTteE6Q2eCQKSqg19j6T+NAAYzSv5R | -action_result.data.\*.conversationId | string | | AAQkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAQAORC3aOpHnZMsHD4-7L40sY= | -action_result.data.\*.conversationIndex | string | | AQHZopYz5ELdo6kedkywcPj/svjSxg== | -action_result.data.\*.createdDateTime | string | | 2023-06-19T10:09:58Z | -action_result.data.\*.flag.flagStatus | string | | notFlagged | -action_result.data.\*.from.emailAddress.address | string | `email` | test@test.com | -action_result.data.\*.from.emailAddress.name | string | | Ryan Edwards | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.id | string | `msgoffice365 message id` | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.get_headers | boolean | | | +action_result.parameter.download_attachments | boolean | | | +action_result.parameter.download_email | boolean | | | +action_result.data.\*.id | string | | | +action_result.data.\*.subject | string | | | +action_result.data.\*.body | string | | | +action_result.data.\*.bodyPreview | string | | | +action_result.data.\*.sender | string | | | +action_result.data.\*.from_field | string | | | +action_result.data.\*.toRecipients | string | | | +action_result.data.\*.ccRecipients | string | | | +action_result.data.\*.bccRecipients | string | | | +action_result.data.\*.receivedDateTime | string | | | +action_result.data.\*.sentDateTime | string | | | action_result.data.\*.hasAttachments | boolean | | True False | -action_result.data.\*.id | string | `msgoffice365 message id` | AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA== | -action_result.data.\*.importance | string | | normal | -action_result.data.\*.inferenceClassification | string | | focused | -action_result.data.\*.internetMessageId | string | `msgoffice365 internet message id` | | -action_result.data.\*.isDeliveryReceiptRequested | boolean | | True False | -action_result.data.\*.isDraft | boolean | | True False | +action_result.data.\*.importance | string | | | action_result.data.\*.isRead | boolean | | True False | -action_result.data.\*.isReadReceiptRequested | boolean | | True False | -action_result.data.\*.lastModifiedDateTime | string | | 2023-06-19T10:09:58Z | -action_result.data.\*.parentFolderId | string | `msgoffice365 folder id` | AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAAAgEPAAAA | -action_result.data.\*.receivedDateTime | string | | 2020-06-18T09:11:31Z | -action_result.data.\*.sender.emailAddress.address | string | `email` | notifications@testdomain.com | -action_result.data.\*.sender.emailAddress.name | string | `email` | notifications@testdomain.com | -action_result.data.\*.sentDateTime | string | | 2023-06-19T10:09:58Z | -action_result.data.\*.subject | string | | test html | -action_result.data.\*.toRecipients.\*.emailAddress.address | string | `email` | test@test.com | -action_result.data.\*.toRecipients.\*.emailAddress.name | string | | Ryan Edwards | -action_result.data.\*.webLink | string | | https://outlook.office365.com/owa/?ItemID=AAkALgAAAAAAHYQDEapmEc2byACqAC%2FEWg0A07XhOkNngkCkqoNfY%2Bk%2FjQAGNNQOowAA&exvsurl=1&viewmodel=ReadMessageItem | -action_result.summary | string | | | -action_result.message | string | | Create time: 2017-10-05T20:19:58Z Subject: Both value are modified Sent time: 2017-10-03T21:31:20Z | +action_result.data.\*.internetMessageId | string | | | +action_result.data.\*.internetMessageHeaders | string | | | +action_result.data.\*.attachments | string | | | +action_result.data.\*.event_id | string | | | summary.total_objects | numeric | | 1 | summary.total_objects_successful | numeric | | 1 | -## action: 'report message' - -Add the sender email into the report +## action: 'list events' -Type: **contain**
-Read only: **False** +List events from user or group calendar -This action processes an email message and updates the sender classification based on the selected verdict (junk, notJunk, phish, unknown, or unknownFutureValue). When enabled, the message can optionally be moved to the corresponding folder based on the verdict. The action applies only if a message from the sender exists in the user's mailbox. +Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**message_id** | required | Message ID to pick the sender of | string | | -**user_id** | required | User ID to base the action of | string | | -**is_message_move_requested** | optional | Indicates whether the message should be moved out of current folder | boolean | | -**report_action** | required | Indicates the type of action to be reported on the message | string | | +**user_id** | optional | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**group_id** | optional | Group ID | string | `msgoffice365 group id` | +**filter** | optional | OData query to filter/search for specific results | string | | +**limit** | optional | Maximum number of events to return | numeric | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.parameter.message_id | string | | | -action_result.parameter.user_id | string | | | -action_result.parameter.is_message_move_requested | boolean | | | -action_result.parameter.report_action | string | | | -action_result.status | string | | success failed | +action_result.status | string | | success failure | action_result.message | string | | | -summary.total_objects | numeric | | | -summary.total_objects_successful | numeric | | | - -## action: 'block sender' +action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.parameter.group_id | string | `msgoffice365 group id` | | +action_result.parameter.filter | string | | | +action_result.parameter.limit | numeric | | | +action_result.data.\*.id | string | | | +action_result.data.\*.subject | string | | | +action_result.data.\*.bodyPreview | string | | | +action_result.data.\*.start | string | | | +action_result.data.\*.end | string | | | +action_result.data.\*.location | string | | | +action_result.data.\*.organizer | string | | | +action_result.data.\*.attendees | string | | | +action_result.data.\*.attendee_list | string | | | +action_result.data.\*.isAllDay | boolean | | True False | +action_result.data.\*.isCancelled | boolean | | True False | +action_result.data.\*.webLink | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | -Add the sender email into the block list +## action: 'get rule' -Type: **contain**
-Read only: **False** +Get the properties and relationships of a messageRule object -This action takes as input an email whose sender will be added to the Block Senders List. The message ID changes after the execution and is a required parameter for request hence undo action would require unique ID. Note that a message from the email address must exist in the user's mailbox before you can add the email address to or remove it from the Blocked Senders List.
  • If the move_to_junk_folder parameter is set to True, the sender of the target email message is added to the blocked sender list and the email message is moved to the Junk Email folder.
  • If the move_to_junk_folder attribute is set to False, the sender of the target email message is added to the blocked sender list and the email message is not moved from the folder.
To view the current Block Senders list, please read the following Powershell articles:
  • https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps
  • https://docs.microsoft.com/en-us/powershell/module/exchange/antispam-antimalware/Get-MailboxJunkEmailConfiguration?view=exchange-ps.
+Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**message_id** | required | Message ID to pick the sender of | string | | -**user_id** | required | User ID to base the action of | string | | -**move_to_junk_folder** | optional | Should the email be moved to the junk folder | boolean | | +**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | +**rule_id** | required | Inbox rule ID | string | `msgoffice365 rule id` | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.parameter.message_id | string | | | -action_result.parameter.move_to_junk_folder | boolean | | | -action_result.parameter.user_id | boolean | | | -action_result.status | string | | success failed | -action_result.summary | string | | | -action_result.status | string | | success failed | +action_result.status | string | | success failure | action_result.message | string | | | -summary.total_objects | numeric | | | -summary.total_objects_successful | numeric | | | - -## action: 'unblock sender' +action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.parameter.rule_id | string | `msgoffice365 rule id` | | +action_result.data.\*.id | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.sequence | numeric | | | +action_result.data.\*.isEnabled | boolean | | True False | +action_result.data.\*.isReadOnly | boolean | | True False | +action_result.data.\*.hasError | boolean | | True False | +action_result.data.\*.conditions | string | | | +action_result.data.\*.actions | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | -Remove the sender email from the block list +## action: 'list rules' -Type: **contain**
-Read only: **False** +Get all the messageRule objects defined for the user's inbox -This action takes as input an email whose sender will be removed from the Block Senders List. The message ID changes after the execution and is a required parameter for request hence undo action would require unique ID. Note that a message from the email address must exist in the user's mailbox before you can add the email address to or remove it from the Blocked Senders List.
  • If the move_to_inbox parameter is set to True, the sender of the target email message is removed from the blocked sender list and the email message is moved from the Junk Email folder.
  • If the move_to_inbox attribute is set to False, the sender of the target email message is removed from the blocked sender list and the email message is not moved from the folder.
To view the current Block Senders list, please read the following Powershell articles:
  • https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps
  • https://docs.microsoft.com/en-us/powershell/module/exchange/antispam-antimalware/Get-MailboxJunkEmailConfiguration?view=exchange-ps.
+Type: **investigate**
+Read only: **True** #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**message_id** | required | Message ID to pick the sender of | string | | -**user_id** | required | User ID to base the action of | string | | -**move_to_inbox** | optional | Should the email be moved to the inbox folder | boolean | | +**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.parameter.message_id | string | | | -action_result.parameter.move_to_inbox | boolean | | | -action_result.parameter.user_id | boolean | | | -action_result.status | string | | success failed | -action_result.summary | string | | | -action_result.status | string | | success failed | +action_result.status | string | | success failure | action_result.message | string | | | -summary.total_objects | numeric | | | -summary.total_objects_successful | numeric | | | +action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | | +action_result.data.\*.id | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.sequence | numeric | | | +action_result.data.\*.isEnabled | boolean | | True False | +action_result.data.\*.isReadOnly | boolean | | True False | +action_result.data.\*.hasError | boolean | | True False | +action_result.data.\*.conditions | string | | | +action_result.data.\*.actions | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | ## action: 'resolve name' -Verify aliases and resolve display names to the appropriate user +Resolve a name to email addresses Type: **investigate**
Read only: **True** -Resolve an Alias name or email address, gathering complex data about the user. - #### Action Parameters PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**email** | required | Name to resolve | string | `email` `string` | +**email_address** | required | User's email address (mailbox) | string | `email` | +**name** | required | Name or email to resolve | string | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.parameter.email | string | `email` `string` | | -action_result.data.\*.id | string | `msgoffice365 id` | | -action_result.data.\*.userPrincipalName | string | `msgoffice365 user principal name` | | -action_result.data.\*.givenName | string | `msgoffice365 given name` | | -action_result.data.\*.surname | string | `msgoffice365 surname` | | -action_result.data.\*.displayName | string | `msgoffice365 display name` | | -action_result.data.\*.mailNickname | string | `msgoffice365 mail nickname` | | -action_result.data.\*.mail | string | `email` | | -action_result.data.\*.otherMails | string | `email list` | | -action_result.data.\*.proxyAddresses | string | `email list` | | -action_result.data.\*.jobTitle | string | `msgoffice365 job title` | | -action_result.data.\*.officeLocation | string | `msgoffice365 office location` | | -action_result.data.\*.value | string | `msgoffice365 user purpose` | | -action_result.data.\*.mobilePhone | string | `msgoffice365 mobile phone` | | -action_result.data.\*.businessPhones | string | `msgoffice365 buisness phones` | | -action_result.data.\*.preferredLanguage | string | `msgoffice365 preferred language` | | -action_result.data.\*.state | string | `msgoffice365 state` | | -action_result.data.\*.postalCode | string | `msgoffice365 postal code` | | -action_result.summary | string | | | -action_result.status | string | | success failed | +action_result.status | string | | success failure | action_result.message | string | | | -summary.total_objects | numeric | | | -summary.total_objects_successful | numeric | | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.name | string | | | +action_result.data.\*.displayName | string | | | +action_result.data.\*.emailAddress | string | | | +action_result.data.\*.userPrincipalName | string | | | +action_result.data.\*.id | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | -## action: 'get mailbox messages' +## action: 'run query' -Retrieves messages from a specified mailbox folder with advanced functionality +Search emails in a mailbox Type: **investigate**
Read only: **True** @@ -2079,144 +1464,73 @@ Read only: **True** PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS --------- | -------- | ----------- | ---- | -------- -**email_address** | required | Email address of the mailbox | string | | -**folder** | optional | Folder to retrieve messages | string | | -**limit** | optional | Maximum number of messages to retrieve (should not exceed 100 per request) | numeric | | -**offset** | optional | Number of messages to skip before retrieving results | numeric | | -**start_date** | optional | Start date for filtering messages (format: YYYY-MM-DD) | string | | -**end_date** | optional | End date for filtering messages (format: YYYY-MM-DD) | string | | -**download_attachments** | optional | Download email attachments to vault | boolean | | -**download_email** | optional | Download email as EML file to vault | boolean | | -**extract_headers** | optional | Include email headers in results | boolean | | -**plus_ingest** | optional | If enabled, messages will be also ingested like on_poll | boolean | | +**email_address** | required | User's email address (mailbox) | string | `email` | +**folder** | optional | Folder name/path or ID | string | | +**get_folder_id** | optional | Retrieve folder ID from folder name/path | boolean | | +**subject** | optional | Substring to search in subject | string | | +**sender** | optional | Sender email to search | string | | +**body** | optional | Substring to search in body | string | | +**internet_message_id** | optional | Internet Message ID to search | string | | +**limit** | optional | Maximum number of emails to return | numeric | | +**search_well_known_folders** | optional | Search in well-known folders | boolean | | #### Action Output DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- -action_result.status | string | | success failed | -action_result.parameter.email_address | string | | | +action_result.status | string | | success failure | +action_result.message | string | | | +action_result.parameter.email_address | string | `email` | | action_result.parameter.folder | string | | | +action_result.parameter.get_folder_id | boolean | | | +action_result.parameter.subject | string | | | +action_result.parameter.sender | string | | | +action_result.parameter.body | string | | | +action_result.parameter.internet_message_id | string | | | action_result.parameter.limit | numeric | | | -action_result.parameter.offset | numeric | | | -action_result.parameter.start_date | string | | | -action_result.parameter.end_date | string | | | +action_result.parameter.search_well_known_folders | boolean | | | action_result.data.\*.id | string | | | -action_result.data.\*.body.content | string | | | -action_result.data.\*.body.contentType | string | | | -action_result.data.\*.flag.flagStatus | string | | | -action_result.data.\*.from.emailAddress.name | string | | | -action_result.data.\*.from.emailAddress.address | string | | | -action_result.data.\*.isRead | boolean | | | -action_result.data.\*.sender.emailAddress.name | string | | | -action_result.data.\*.sender.emailAddress.address | string | | | -action_result.data.\*.isDraft | boolean | | | -action_result.data.\*.replyTo.\*.emailAddress.address | string | | | -action_result.data.\*.replyTo.\*.emailAddress.name | string | | | action_result.data.\*.subject | string | | | -action_result.data.\*.webLink | string | `url` | | -action_result.data.\*.changeKey | string | | | -action_result.data.\*.categories.\*.name | string | | | -action_result.data.\*.importance | string | | | -action_result.data.\*.uniqueBody.content | string | | | -action_result.data.\*.uniqueBody.contentType | string | | | -action_result.data.\*.bodyPreview | string | | | -action_result.data.\*.ccRecipients.\*.emailAddress.address | string | | | -action_result.data.\*.ccRecipients.\*.emailAddress.name | string | | | -action_result.data.\*.sentDateTime | string | | | -action_result.data.\*.toRecipients.\*.emailAddress.name | string | | | -action_result.data.\*.toRecipients.\*.emailAddress.address | string | | | -action_result.data.\*.bccRecipients.\*.emailAddress.address | string | | | -action_result.data.\*.bccRecipients.\*.emailAddress.name | string | | | -action_result.data.\*.conversationId | string | | | -action_result.data.\*.hasAttachments | boolean | | | -action_result.data.\*.parentFolderId | string | | | -action_result.data.\*.createdDateTime | string | | | +action_result.data.\*.sender | string | | | action_result.data.\*.receivedDateTime | string | | | -action_result.data.\*.conversationIndex | string | | | +action_result.data.\*.bodyPreview | string | | | +action_result.data.\*.hasAttachments | boolean | | True False | action_result.data.\*.internetMessageId | string | | | -action_result.data.\*.lastModifiedDateTime | string | | | -action_result.data.\*.internetMessageHeaders.\*.name | string | | | -action_result.data.\*.internetMessageHeaders.\*.value | string | | | -action_result.data.\*.internetMessageHeaders.Accept-Language | string | | en-US | -action_result.data.\*.internetMessageHeaders.Authentication-Results | string | | spf=pass (sender IP is 209.85.210.171) smtp.mailfrom=testdomain.com; .abc.com; dkim=pass (signature was verified) header.d=testdomain.com.20150623.gappssmtp.com;.abc.com; dmarc=pass action=none header.from=testdomain.com;compauth=pass reason=100 | -action_result.data.\*.internetMessageHeaders.Content-Language | string | | en-US | -action_result.data.\*.internetMessageHeaders.Content-Transfer-Encoding | string | | binary | -action_result.data.\*.internetMessageHeaders.Content-Type | string | | multipart/related | -action_result.data.\*.internetMessageHeaders.DKIM-Signature | string | | v=1; a=rsa-sha256; c=relaxed/relaxed; d=testdomain.com.20150623.gappssmtp.com; s=20150623; h=message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=avAAeJ8jF08K4oIBhxTirRmyB+SXHwdU0zdxv7eqs/zWaWWcgmT0007KP560TTgo5u oD4nb6TvKxpRyWW4QwmkbuMIwHsMvehd2l1gispV3AawyGJjpmN7ErVYfLtIkz2Tap3V YxmluV+SqeyyxTU8pFAEZ7+2C2lOb1DO5TC7xCMv+dyzevSscJdbeN0dFkG+C93zCqkg w2fxubx2HDD7b/U6m2wXllYhH608wKJ/qYzyvQyqxYqNiQOtPRg2gw4sZ2UgN3+UQyVq 8ubO39ZuqakJpzEzYMw10d6E7SQhvHDJH7mFwhBlzhvOpb2gLJDN8n8dJaZo05BozQqq MsvA== | -action_result.data.\*.internetMessageHeaders.Date | string | | Thu, 18 Jun 2020 02:11:26 -0700 | -action_result.data.\*.internetMessageHeaders.From | string | | "Test" | -action_result.data.\*.internetMessageHeaders.In-Reply-To | string | | | -action_result.data.\*.internetMessageHeaders.MIME-Version | string | | 1.0 | -action_result.data.\*.internetMessageHeaders.Message-ID | string | | <5eeb2fbe.1c69fb81.22b4b.676a@mx.test.com> | -action_result.data.\*.internetMessageHeaders.Received | string | | from localhost.localdomain (host-240.test.com. [204.107.141.240]) by tset.abc.com with UTF8SMTPSA id ng12sm1923252pjb.15.2020.06.18.02.11.26 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jun 2020 02:11:26 -0700 (PDT) | -action_result.data.\*.internetMessageHeaders.Received-SPF | string | | Pass (protection.test.com: domain of testdomain.com designates 209.85.210.171 as permitted sender) receiver=protection.test.com; client-ip=209.85.210.171; helo=mail-pf1-f171.test.com; | -action_result.data.\*.internetMessageHeaders.References | string | | | -action_result.data.\*.internetMessageHeaders.Return-Path | string | `email` | notifications@testdomain.com | -action_result.data.\*.internetMessageHeaders.Subject | string | | Fw: Email having different attachments | -action_result.data.\*.internetMessageHeaders.Thread-Index | string | | AQHWZLqyXR4k4Sc6skyFCMPITcMsbKpGS7Bm | -action_result.data.\*.internetMessageHeaders.Thread-Topic | string | | Email having different attachments | -action_result.data.\*.internetMessageHeaders.To | string | | "Test" | -action_result.data.\*.internetMessageHeaders.X-EOPAttributedMessage | string | | 0 | -action_result.data.\*.internetMessageHeaders.X-EOPTenantAttributedMessage | string | | a417c578-c7ee-480d-a225-d48057e74df5:0 | -action_result.data.\*.internetMessageHeaders.X-Forefront-Antispam-Report | string | | CIP:209.85.210.171;CTRY:US;LANG:en;SCL:-1;SRV:;IPV:NLI;SFV:SFE;H:mail-pf1-f171.test.com;PTR:mail-pf1-f171.test.com;CAT:NONE;SFTY:;SFS:;DIR:INB;SFP:; | -action_result.data.\*.internetMessageHeaders.X-Gm-Message-State | string | | AOAM533ynFERIhSIewEEkj4b8B1rPNOEeie1IxBdrd55treEMtBa1jkL cO5ee4Ff6p0FYedfFtVtHKiCglGTpFTOSw== | -action_result.data.\*.internetMessageHeaders.X-Google-DKIM-Signature | string | | v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=fPT47NIiheeY6GM0bxUOlsmnOgN4WuiOlalFvZqrAiFiOoYk6zrznvgIcAtiHZ4nxE naQAa+mZs5svqRjib3YI52OvR5U8MitIYaa0Rt3LyYSUO1s3iKTUs4nHyRnqPt1skNl7 2OUwsZPXo3ShJDw/uxZRu/cuN1iIfeuE02PrbR04p4D8+1XRslqt/Xqm/bOWKUauqZWe dH1E7meFY01hXxODreO4nWHIhsZgr49TpP/OqRyFcyKHHFFg2sPGXz+QNah6jP4YQUYd Tty2wzOX3nc/YS7TkVo3ORmbzh9o+UZaqH8wHbQlyTdklYxoMPvJwZTo72rTxZeqiJ9E J7PQ== | -action_result.data.\*.internetMessageHeaders.X-Google-Smtp-Source | string | | ABdhPJxrYC7raBubCCIOmauxmxryzS9KsihTN6XCRgaNp2rDrG71TVxryzYCtelFOZ2Xj1LzcYIiMA== | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData | string | | VSM9HTzub/OH3NCwKXEQqkkzjnhdw5kXsgd9WM0SRgZ0qRdPg5D9/o3LA7lf8ziXc5k0mm9M5mHvFoYePXNXs/MGhGdBGxa/qUQ+FVHA2mDgfPkamJCEZxz//OX/uruTDo+zF4p9D1dQJpnIpx1M75OhuvrHX/BxWWzyAh78DXfF214YHdyFBCYepwl56CS7+fSGQL/r3p+OvWIBnIkISC+HJljSro2k47pPPAkspMhoUkb+zklyENFjez+JcEHYlih2FiNeUO8kb9b7qvlm3zPK98HLspzDh4BojpQ6Ff330iy7nfIK726tCMByxjOdnEQSB9Ua2sbE5gxSeeWL8MB5DHcQSSsXg+sR8w4gXrXLO3meE0lNQKRoAv2b1U0Q+yM0QBqeQWlymZG21bKeuH4gtAFQvfXNjoCtIbBQK1n7ZnL7fI21FJZRcMcKEneus6gLYUqD4PdLEq9FEGbfgiLmVYeUAL2A0Q/gectvL1OVudtHVR5gFMJKt65F1OtS04CPulfLLFSl1F4AzpjjtBSyQcK9R7bOsjoHxQXPMd9fMCzMSIq5f551pO0klKqWY7l11Un2Noj6CA7EtXiD1bTv8JmYQEKR+0HTZagNd+79GeTvKjxTvt9MkyO8k3aqWyNqT331ITnVICtksN1TVMCp8GVeDudNMr2PLSW0alOduR5unuEgTWrqHoaTGOovQx0PVjudNlpZ80ANK9hqaC/ZhLLOtNpJ3fZnjs06PzrPLGhE/IeccY1n8sYDvGm1QA9TN6JaaGPl1Pj6ecy16k0XuF/PKGHTL0M4LCpxSS6T87oFFH1zHkKtmbJp3aAI4bt3ihbQmwFb29JyMgL7ZOy+zrIwXGILh1KQGWQQv1uXXnAuqQy29HeFXs6D2hDHxHlBk5ZQ+vgRtsvRvGnq58vJ3CapjntfL3pOINUj1avLyAZxjasBWMTwaZs9JQ4ZIMekzkIk05lh9XfDSeULk2yKaH8YSCC6ENUHxSWa6pPHJfOdp9kXwOtlp09/VTTAikKy862k9ybN4bRWZB45B9Pv5scna8IX3rthIXUih8c= | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthAs | string | | Internal | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthSource | string | | SJ0QA11MB4941.namprd11.prod.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-FromEntityHeader | string | | Internet | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Id | string | | a417c578-c7ee-480d-a225-d48057e74df5 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-MailboxType | string | | HOSTED | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Network-Message-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-OriginalArrivalTime | string | | 18 Jun 2020 09:11:28.2511 (UTC) | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-CrossTenant-UserPrincipalName | string | | bs91VnpEPjrqCnvlIeymwO6ye4Q8rggHggVNUPUbV/tC9uuFPVFOYg7e/Cd0MeGmSqT4AlLW0Nn4ZeEqNieSf/D1gp5iLz/YkwjXhYUSJnLRb/csQN4sRMMZsX3LUkKkwVpifaeJzoukLu8qSWn7og== | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthAs | string | | Anonymous | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthMechanism | string | | 04 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-AuthSource | string | | DM6NAM11FT055.eop-nam11.prod.protection.test.com | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationInterval | string | | 1:00:00:00.0000000 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationIntervalReason | string | | OriginalSubmit | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTime | string | | 18 Jun 2020 09:11:28.2531 (UTC) | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTimeReason | string | | OriginalSubmit | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-MessageDirectionality | string | | Incoming | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-Network-Message-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Organization-SCL | string | | -1 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Processed-By-BccFoldering | string | | 15.20.3109.017 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStamped | string | | BN6PR18MB1492 | -action_result.data.\*.internetMessageHeaders.X-MS-Exchange-Transport-EndToEndLatency | string | | 00:00:02.7417647 | -action_result.data.\*.internetMessageHeaders.X-MS-Has-Attach | string | | yes | -action_result.data.\*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id | string | | 4b1ef179-4fe7-4248-7ec0-08d81367956e | -action_result.data.\*.internetMessageHeaders.X-MS-Oob-TLC-OOBClassifiers | string | | OLM:1728; | -action_result.data.\*.internetMessageHeaders.X-MS-PublicTrafficType | string | | Email | -action_result.data.\*.internetMessageHeaders.X-MS-TNEF-Correlator | string | | | -action_result.data.\*.internetMessageHeaders.X-MS-TrafficTypeDiagnostic | string | | BN6PR18MB1492: | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam | string | | BCL:0; | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Mailbox-Delivery | string | | wl:1;pcwl:1;ucf:0;jmr:0;auth:0;dest:I;ENG:(750128)(520011016)(520004050)(702028)(944506458)(944626604); | -action_result.data.\*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info | string | | La+CSxAnpzVJOXq7njrFPhIbsh0khleSwldy+W8NYDRsoyyPruPIiId4Avama7JyfzrxoExzhLk5pDn2lGPAJIpdcguiDSsDQg5T+iBCJgFeaEJXjhstECMi842/JGawB9WsiGw9Q/PpvjO5H/2fNLlQZVZW3AAQVZSsX3az4iOsv1Ggj4aYZRMKHmPAtniWOEtQD7zAEWC0jIZf613lWy3vxHfb/3+pV9X8zqPqazbyGy5Q14PICSNkKnvIw8rmeqJV8eSHhvR51Lchib6OIN4xOpLWxkSkBTt5B95RUPnpgPvgp2yLo0Q+EYRIabLDQ0kMsv+24+RnFmr9vo2gRNuFusw8iEPsVEQyhfgIWtBtsBpyvyykxcfa6lIdzQhixZH3Tlkdh1kb15wFS3Ooz3CjaWbY8jcUot5l1p08Ypsj6r7CpIo3xE6jE0x/EeUkDK3Fu/Ol0pOsJ1N5W4iJLdjqSQM3l/t9QWlcPhD8s6D7D7JM5OUHCeFEPr7sSL+P/5zTgBaeUvwtZrlQSH2GHc+5gPW8rkwlwJLJftVEid0gO2PUOrzItzME5PXYAcdx++sF3XC1YMPLet/jMpX8T7/z7+hxFxNyifgmGJ+DkNOec7yGkkcLBz6iCaHx7OrRGwDHIcdAtV85wCk3NEDDiKyHivQpwp/gY55W+wkLe7aqSHmFzm1rUSslx+DWz8w2EgSjJxOmf0JkoNKbTFl3FObkocR0lUUQUnETuoAXUqvpWGD5B69W9XXUM8c43ozz2oBZseheSAtkLil3tMIr/CMCMILPX/LdoErNtkmiFXCPqaLFSSeyO61oCMl6Ezndtwp22nwMPUg5ofG0kdqFuTW122umhy9C6h5BcREaLhWclSyqDoZPB9RvkRlI2kTRwuwbuFW3iOMzmVwxLIQH9K5JkxdMvC3hvNpjVgz7Q2ZnEF3xSNqeoWVQvkaIe8rQLUc8s+HMRUmSERGdfSuQJAx47g8PDs9s3rS/ThUSzIaljJPbUgXEnFg/G6h3I/yXLj2Nj2OG50snoI5jJmE4+69YmNwasdDZuYpnuQeFgu11HtsLniDthJdjEJyYC1utZNt9hgA+6JlLnm7Dxb43cSIiW8ev+3X+b2kREj2k/m8fSz7YgtoCB8AkuiVXRaH3EUiq8XCExbbWeynKRgwCZ6bzvfSiT3+cg+QQKPHFc/cgot56ta6X80tjhFodpTQNTE6V6C9QFHJ3JCVhsSzVifJAc8crI5hAcPbKFEIjinENcfpF/8reo2Yr1xFElhoX | -action_result.data.\*.internetMessageHeaders.X-Originating-IP | string | | [2.39.180.162] | -action_result.data.\*.internetMessageHeaders.X-Received | string | | by 2002:aa7:84d9:: with SMTP id x25mr2807688pfn.300.1592471487394; Thu, 18 Jun 2020 02:11:27 -0700 (PDT) | -action_result.data.\*.internetMessageHeaders.subject | string | | test html | -action_result.data.\*.isReadReceiptRequested | boolean | | | -action_result.data.\*.inferenceClassification | string | | | -action_result.data.\*.isDeliveryReceiptRequested | boolean | | | -action_result.summary.total_messages | numeric | | | -action_result.summary.duplicate_emails | numeric | | | -action_result.summary.failed_emails | numeric | | | -action_result.summary.new_emails_ingested | numeric | | | +action_result.data.\*.parentFolderId | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | + +## action: 'block sender' + +Add a sender to the blocked senders list + +Type: **contain**
+Read only: **True** + +#### Action Parameters + +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**email_address** | required | User's email address (mailbox) | string | `email` | +**sender** | required | Email address of sender to block | string | `email` | + +#### Action Output + +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failure | action_result.message | string | | | -summary.total_objects | numeric | | | -summary.total_objects_successful | numeric | | | -action_result.parameter.download_attachments | boolean | | | -action_result.parameter.download_email | boolean | | | -action_result.parameter.extract_headers | boolean | | | -action_result.parameter.plus_ingest | boolean | | | +action_result.parameter.email_address | string | `email` | | +action_result.parameter.sender | string | `email` | | +action_result.data.\*.message | string | | | +summary.total_objects | numeric | | 1 | +summary.total_objects_successful | numeric | | 1 | ______________________________________________________________________ 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. diff --git a/office365.json b/office365.json deleted file mode 100644 index 2b1aa54..0000000 --- a/office365.json +++ /dev/null @@ -1,8805 +0,0 @@ -{ - "appid": "0a0a4087-10e8-4c96-9872-b740ff26d8bb", - "name": "MS Graph for Office 365", - "description": "This app connects to Office 365 using the MS Graph API to support investigate and generic actions related to the email messages and calendar events", - "type": "email", - "product_vendor": "Microsoft", - "logo": "logo_microsoftoffice365.svg", - "logo_dark": "logo_microsoftoffice365_dark.svg", - "product_name": "Office 365 (MS Graph)", - "product_version_regex": ".*", - "publisher": "Splunk", - "contributors": [ - { - "name": "Alan Shaikh" - }, - { - "name": "Chris Hutto" - }, - { - "name": "Mathieu A. Cormier" - }, - { - "name": "Dallan Wagner" - }, - { - "name": "Mhike Funderburk" - }, - { - "name": "Maciej Walendziuk" - }, - { - "name": "Anton Neledov" - } - ], - "license": "Copyright (c) 2017-2025 Splunk Inc.", - "app_version": "4.1.0", - "utctime_updated": "2026-01-09T05:33:39.032641Z", - "package_name": "phantom_msgraphoffice365", - "main_module": "office365_connector.py", - "min_phantom_version": "6.3.0", - "latest_tested_versions": [ - "Cloud 13 Feb, 2025" - ], - "app_wizard_version": "1.0.0", - "python_version": "3.9, 3.13", - "fips_compliant": true, - "rest_handler": "office365_connector.handle_request", - "configuration": { - "tenant": { - "description": "Tenant ID (e.g. 1e309abf-db6c-XXXX-a1d2-XXXXXXXXXXXX)", - "data_type": "string", - "required": true, - "order": 0 - }, - "client_id": { - "description": "Application ID", - "data_type": "string", - "required": true, - "order": 1 - }, - "auth_type": { - "data_type": "string", - "order": 2, - "required": true, - "description": "Authentication type to use for connectivity", - "default": "Automatic", - "value_list": [ - "Automatic", - "OAuth", - "Certificate Based Authentication(CBA)" - ] - }, - "ph": { - "data_type": "ph", - "order": 3 - }, - "client_secret": { - "description": "Application Secret(required for OAuth)", - "data_type": "password", - "order": 4 - }, - "ph_4": { - "data_type": "ph", - "order": 5 - }, - "certificate_thumbprint": { - "description": "Certificate Thumbprint (required for CBA)", - "data_type": "password", - "order": 6, - "required": false - }, - "certificate_private_key": { - "description": "Certificate Private Key (.PEM)", - "data_type": "password", - "required": false, - "order": 7 - }, - "admin_access": { - "description": "Admin Access Required", - "data_type": "boolean", - "default": true, - "order": 8 - }, - "admin_consent": { - "description": "Admin Consent Already Provided (Required checked for CBA)", - "data_type": "boolean", - "default": false, - "order": 9 - }, - "scope": { - "description": "Access Scope (for use with OAuth non-admin access; space-separated)", - "data_type": "string", - "default": "https://graph.microsoft.com/Calendars.Read https://graph.microsoft.com/User.Read", - "order": 10 - }, - "ph_2": { - "data_type": "ph", - "order": 11 - }, - "email_address": { - "description": "Email Address of the User (On Poll)", - "data_type": "string", - "order": 12 - }, - "folder": { - "description": "Mailbox folder name/folder path or the internal office365 folder ID to ingest (On Poll)", - "data_type": "string", - "order": 13 - }, - "get_folder_id": { - "description": "Retrieve the folder ID for the provided folder name/folder path automatically and replace the folder parameter value (On Poll)", - "data_type": "boolean", - "default": true, - "order": 14 - }, - "ph_3": { - "data_type": "ph", - "order": 15 - }, - "first_run_max_emails": { - "description": "Maximum Containers for scheduled polling first time", - "data_type": "numeric", - "default": 1000, - "order": 16 - }, - "max_containers": { - "description": "Maximum Containers for scheduled polling", - "data_type": "numeric", - "default": 100, - "order": 17 - }, - "extract_attachments": { - "description": "Extract Attachments", - "data_type": "boolean", - "default": false, - "order": 18 - }, - "extract_urls": { - "description": "Extract URLs", - "data_type": "boolean", - "default": false, - "order": 19 - }, - "extract_ips": { - "description": "Extract IPs", - "data_type": "boolean", - "default": false, - "order": 20 - }, - "extract_domains": { - "description": "Extract Domain Names", - "data_type": "boolean", - "default": false, - "order": 21 - }, - "extract_hashes": { - "description": "Extract Hashes", - "data_type": "boolean", - "default": false, - "order": 22 - }, - "ingest_eml": { - "description": "Ingest EML file for the itemAttachment", - "data_type": "boolean", - "default": false, - "order": 23 - }, - "ingest_manner": { - "data_type": "string", - "description": "How to Ingest (during ingestion, should the app get the latest emails or the oldest)", - "order": 24, - "value_list": [ - "oldest first", - "latest first" - ], - "default": "oldest first" - }, - "retry_count": { - "description": "Maximum attempts to retry the API call (Default: 3)", - "data_type": "numeric", - "default": 3, - "order": 25 - }, - "retry_wait_time": { - "description": "Delay in seconds between retries (Default: 60)", - "data_type": "numeric", - "default": 60, - "order": 26 - }, - "extract_eml": { - "data_type": "boolean", - "description": "Extract root (primary) email as Vault", - "order": 27, - "default": false - } - }, - "actions": [ - { - "action": "test connectivity", - "identifier": "test_connectivity", - "description": "Use supplied credentials to generate a token with MS Graph", - "type": "test", - "read_only": true, - "parameters": {}, - "output": [], - "versions": "EQ(*)" - }, - { - "action": "generate token", - "identifier": "generate_token", - "description": "Generate a token", - "type": "generic", - "read_only": false, - "parameters": {}, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ], - "column_name": "Status", - "column_order": 0 - }, - { - "data_path": "action_result.data", - "data_type": "string" - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Token generated" - ], - "column_name": "Message", - "column_order": 1 - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "type": "table" - }, - "versions": "EQ(*)" - }, - { - "action": "oof check", - "identifier": "oof_check", - "description": "Get user's out of office status", - "type": "investigate", - "read_only": true, - "parameters": { - "user_id": { - "description": "User ID/Principal name", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ], - "order": 0 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "string", - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ], - "column_name": "User ID/Principal Name", - "column_order": 0, - "example_values": [ - "eeb3645f-df19-58a1-0e9c-ghi234cb5f6f" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/$metadata#users('EXAMPLEUSERID')/mailboxSettings/automaticRepliesSetting" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.externalAudience", - "data_type": "string", - "example_values": [ - "all" - ] - }, - { - "data_path": "action_result.data.*.externalReplyMessage", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.internalReplyMessage", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.scheduledEndDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2022-03-15T12:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.scheduledEndDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.scheduledStartDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2022-03-14T12:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.scheduledStartDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.status", - "data_type": "string", - "column_name": "Out Of Office Status", - "column_order": 1, - "example_values": [ - "alwaysEnabled" - ] - }, - { - "data_path": "action_result.summary.events_matched", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved out of office status" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 4, - "title": "Out of Office Status", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "list events", - "identifier": "list_events", - "description": "List events from user or group calendar", - "type": "investigate", - "read_only": true, - "parameters": { - "user_id": { - "description": "User ID/Principal name", - "data_type": "string", - "primary": true, - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ], - "order": 0 - }, - "group_id": { - "description": "Group ID", - "data_type": "string", - "primary": true, - "contains": [ - "msgoffice365 group id" - ], - "order": 1 - }, - "filter": { - "description": "OData query to filter/search for specific results", - "data_type": "string", - "order": 2 - }, - "limit": { - "description": "Maximum number of events to return", - "data_type": "numeric", - "order": 3 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.filter", - "data_type": "string", - "example_values": [ - "$filter=subject eq 'Test2'" - ] - }, - { - "data_path": "action_result.parameter.group_id", - "data_type": "string", - "contains": [ - "msgoffice365 group id" - ], - "example_values": [ - "3d9c58f8-9f38-4016-93ac-b61095f31c48" - ] - }, - { - "data_path": "action_result.parameter.limit", - "data_type": "numeric", - "example_values": [ - 20 - ] - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "string", - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"b1MzKFCcdkuJ24Mc2VsdjwABAdhQhg==\"" - ] - }, - { - "data_path": "action_result.data.*.allowNewTimeProposals", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.attendee_list", - "data_type": "string", - "column_name": "Attendee(s)", - "column_order": 3, - "example_values": [ - "H-test, o365group" - ] - }, - { - "data_path": "action_result.data.*.attendees.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "H-test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.attendees.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "H-test" - ] - }, - { - "data_path": "action_result.data.*.attendees.*.status.response", - "data_type": "string", - "example_values": [ - "none" - ] - }, - { - "data_path": "action_result.data.*.attendees.*.status.time", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00Z" - ] - }, - { - "data_path": "action_result.data.*.attendees.*.type", - "data_type": "string", - "example_values": [ - "required" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`\\r\\n\\r\\n\\r\\n\\r\\n
 
\\r\\n\\r\\n\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "html" - ] - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.calendar@odata.associationLink", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/users('EXAMPLEUSERID')/calendars('EXAMPLECALENDERID')/$ref" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.calendar@odata.navigationLink", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/users('EXAMPLEUSERID')/calendars('EXAMPLECALENDERID')" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.categories.*.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string", - "example_values": [ - "b1MzKFCcdkuJ24Mc2VsdjwABAdhQhg==" - ] - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2019-10-03T09:03:42.4958512Z" - ] - }, - { - "data_path": "action_result.data.*.end.dateTime", - "data_type": "string", - "column_name": "End Time", - "column_order": 2, - "example_values": [ - "2019-10-04T15:30:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.end.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.hideAttendees", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.iCalUId", - "data_type": "string", - "example_values": [ - "040000008200E00074C5B7101A82E00800000000347B5D74C979D5010000000000000000100000003F2152B556F23543B1B9C751CCD711A3" - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "contains": [ - "msgoffice365 event id" - ], - "example_values": [ - "AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAENAABvUzMoUJx2S4nbgxzZWx2PAAEB90vfAAA=" - ] - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.isAllDay", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isCancelled", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isOnlineMeeting", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isOrganizer", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReminderOn", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2019-10-04T15:24:43.0639836Z" - ] - }, - { - "data_path": "action_result.data.*.location.address.city", - "data_type": "string", - "example_values": [ - "City" - ] - }, - { - "data_path": "action_result.data.*.location.address.countryOrRegion", - "data_type": "string", - "example_values": [ - "Country" - ] - }, - { - "data_path": "action_result.data.*.location.address.postalCode", - "data_type": "string", - "example_values": [ - "245004" - ] - }, - { - "data_path": "action_result.data.*.location.address.state", - "data_type": "string", - "example_values": [ - "State" - ] - }, - { - "data_path": "action_result.data.*.location.address.street", - "data_type": "string", - "example_values": [ - "Location Address" - ] - }, - { - "data_path": "action_result.data.*.location.coordinates.latitude", - "data_type": "numeric", - "example_values": [ - 23.0011 - ] - }, - { - "data_path": "action_result.data.*.location.coordinates.longitude", - "data_type": "numeric", - "example_values": [ - 72.4994 - ] - }, - { - "data_path": "action_result.data.*.location.displayName", - "data_type": "string", - "example_values": [ - "Test" - ] - }, - { - "data_path": "action_result.data.*.location.locationType", - "data_type": "string", - "example_values": [ - "default" - ] - }, - { - "data_path": "action_result.data.*.location.locationUri", - "data_type": "string", - "example_values": [ - "https://www.bingapis.com/api/v6/localbusinesses/YN4070x2912827763012539383?setLang=en" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.location.uniqueId", - "data_type": "string", - "example_values": [ - "f30c3e81-78b7-4f47-8890-f60c3f57e199" - ] - }, - { - "data_path": "action_result.data.*.location.uniqueIdType", - "data_type": "string", - "example_values": [ - "unknown" - ] - }, - { - "data_path": "action_result.data.*.locations.*.address.city", - "data_type": "string", - "example_values": [ - "City" - ] - }, - { - "data_path": "action_result.data.*.locations.*.address.countryOrRegion", - "data_type": "string", - "example_values": [ - "Country" - ] - }, - { - "data_path": "action_result.data.*.locations.*.address.postalCode", - "data_type": "string", - "example_values": [ - "245004" - ] - }, - { - "data_path": "action_result.data.*.locations.*.address.state", - "data_type": "string", - "example_values": [ - "State" - ] - }, - { - "data_path": "action_result.data.*.locations.*.address.street", - "data_type": "string", - "example_values": [ - "Location Address" - ] - }, - { - "data_path": "action_result.data.*.locations.*.coordinates.latitude", - "data_type": "numeric", - "example_values": [ - 23.0011 - ] - }, - { - "data_path": "action_result.data.*.locations.*.coordinates.longitude", - "data_type": "numeric", - "example_values": [ - 72.4994 - ] - }, - { - "data_path": "action_result.data.*.locations.*.displayName", - "data_type": "string", - "example_values": [ - "Test Building Address Bus Stop" - ] - }, - { - "data_path": "action_result.data.*.locations.*.locationType", - "data_type": "string", - "example_values": [ - "localBusiness" - ] - }, - { - "data_path": "action_result.data.*.locations.*.locationUri", - "data_type": "string", - "example_values": [ - "https://www.bingapis.com/api/v6/localbusinesses/YN4070x2912827763012539383?setLang=en" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.locations.*.uniqueId", - "data_type": "string", - "example_values": [ - "f30c3e81-78b7-4f47-8890-f60c3f57e199" - ] - }, - { - "data_path": "action_result.data.*.locations.*.uniqueIdType", - "data_type": "string", - "example_values": [ - "locationStore" - ] - }, - { - "data_path": "action_result.data.*.occurrenceId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onlineMeeting", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onlineMeetingProvider", - "data_type": "string", - "example_values": [ - "unknown" - ] - }, - { - "data_path": "action_result.data.*.onlineMeetingUrl", - "data_type": "string", - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.organizer.emailAddress.address", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.organizer.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.originalEndTimeZone", - "data_type": "string", - "example_values": [ - "Pacific Standard Time" - ] - }, - { - "data_path": "action_result.data.*.originalStartTimeZone", - "data_type": "string", - "example_values": [ - "Pacific Standard Time" - ] - }, - { - "data_path": "action_result.data.*.recurrence", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.reminderMinutesBeforeStart", - "data_type": "numeric", - "example_values": [ - 15 - ] - }, - { - "data_path": "action_result.data.*.responseRequested", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.responseStatus.response", - "data_type": "string", - "example_values": [ - "organizer" - ] - }, - { - "data_path": "action_result.data.*.responseStatus.time", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00Z" - ] - }, - { - "data_path": "action_result.data.*.sensitivity", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.seriesMasterId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.showAs", - "data_type": "string", - "example_values": [ - "busy" - ] - }, - { - "data_path": "action_result.data.*.start.dateTime", - "data_type": "string", - "column_name": "Start Time", - "column_order": 1, - "example_values": [ - "2019-10-04T15:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.start.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "column_name": "Subject", - "column_order": 0, - "example_values": [ - "New event - 1" - ] - }, - { - "data_path": "action_result.data.*.transactionId", - "data_type": "string", - "example_values": [ - "b2e47e5d-8f87-9845-c507-7be56490c432" - ] - }, - { - "data_path": "action_result.data.*.type", - "data_type": "string", - "example_values": [ - "singleInstance" - ] - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "contains": [ - "url" - ], - "example_values": [ - "https://outlook.office365.com/owa/?itemid=AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAENAABvUzMoUJx2S4nbgxzZWx2PAAEB90vfAAA%3D&exvsurl=1&path=/calendar/item" - ] - }, - { - "data_path": "action_result.data.locations.*.displayName", - "data_type": "string" - }, - { - "data_path": "action_result.summary.events_matched", - "data_type": "numeric", - "example_values": [ - 8 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved 8 events" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Calendar Events", - "type": "custom", - "height": 5, - "view": "office365_view.display_view" - }, - "versions": "EQ(*)" - }, - { - "action": "get rule", - "identifier": "get_rule", - "description": "Get the properties and relationships of a messageRule object", - "type": "investigate", - "read_only": true, - "parameters": { - "user_id": { - "description": "User ID/Principal name", - "data_type": "string", - "required": true, - "primary": true, - "order": 0, - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ] - }, - "rule_id": { - "description": "Inbox rule ID", - "data_type": "string", - "required": true, - "primary": true, - "order": 1, - "contains": [ - "msgoffice365 rule id" - ] - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.rule_id", - "data_type": "string", - "example_values": [ - "AQAABgFGMAc=" - ], - "contains": [ - "msgoffice365 rule id" - ] - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://graph.microsoft.com/v1.0/$metadata#users('eeb3645f-df19-47a1-8e8c-fcd234cb5f6f')/mailFolders('inbox')/messageRules/$entity" - ] - }, - { - "data_path": "action_result.data.*.actions_copyToFolder", - "data_type": "string", - "example_values": [ - "AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAF6qrTswAAAA==" - ] - }, - { - "data_path": "action_result.data.*.actions_stopProcessingRules", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.conditions_fromAddresses_0_emailAddress_address", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@test.com" - ] - }, - { - "data_path": "action_result.data.*.conditions_fromAddresses_0_emailAddress_name", - "data_type": "string", - "example_values": [ - "Ryan Edwards" - ] - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "example_values": [ - "Move all messages from Casey Edwards to test-msgoffice365-test" - ] - }, - { - "data_path": "action_result.data.*.hasError", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "AQAABgFGL8A=" - ] - }, - { - "data_path": "action_result.data.*.isEnabled", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadOnly", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.sequence", - "data_type": "numeric", - "example_values": [ - 2 - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved specified inbox rule" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved specified inbox rule" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "get rule", - "type": "custom", - "height": 5, - "view": "office365_view.display_view" - }, - "versions": "EQ(*)" - }, - { - "action": "list rules", - "identifier": "list_rules", - "description": "Get all the messageRule objects defined for the user's inbox", - "type": "investigate", - "read_only": true, - "parameters": { - "user_id": { - "description": "User ID/Principal name", - "data_type": "string", - "required": true, - "primary": true, - "order": 0, - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ] - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ] - }, - { - "data_path": "action_result.data.*.actions.copyToFolder", - "data_type": "string", - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAF6qrTswAAAA==" - ] - }, - { - "data_path": "action_result.data.*.actions.delete", - "data_type": "boolean", - "example_values": [ - true, - false - ], - "column_name": "Delete Email Enabled", - "column_order": 2 - }, - { - "data_path": "action_result.data.*.actions.markAsRead", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.actions.moveToFolder", - "data_type": "string", - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAF6qrTtAAAAA==" - ] - }, - { - "data_path": "action_result.data.*.actions.stopProcessingRules", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.conditions.fromAddresses.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.conditions.fromAddresses.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Ryan Edwards" - ] - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "example_values": [ - "Emails to Trash" - ], - "column_name": "Rule Name", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.hasError", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "column_name": "Rule ID", - "example_values": [ - "AQAABiQdmB8=" - ], - "contains": [ - "msgoffice365 rule id" - ], - "column_order": 1 - }, - { - "data_path": "action_result.data.*.isEnabled", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadOnly", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.sequence", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "action_result.summary.total_rules_returned", - "data_type": "numeric", - "example_values": [ - 14 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved 7 rules" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "title": "list rules", - "type": "custom", - "view": "office365_view.display_view" - }, - "versions": "EQ(*)" - }, - { - "action": "list users", - "identifier": "list_users", - "description": "Retrieve a list of users", - "type": "investigate", - "read_only": true, - "parameters": { - "filter": { - "description": "Search for specific results", - "data_type": "string", - "order": 0 - }, - "limit": { - "description": "Maximum number of users to return", - "data_type": "numeric", - "order": 1 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.filter", - "data_type": "string", - "example_values": [ - "displayName eq 'User Name'" - ] - }, - { - "data_path": "action_result.parameter.limit", - "data_type": "numeric", - "example_values": [ - 20 - ] - }, - { - "data_path": "action_result.data.*.businessPhones", - "data_type": "string", - "example_values": [ - "2056120271" - ] - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "example_values": [ - "Test Admin" - ], - "column_name": "Display Name", - "column_order": 2 - }, - { - "data_path": "action_result.data.*.givenName", - "data_type": "string", - "example_values": [ - "Test" - ], - "column_name": "Given Name", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "6132ca31-7a09-434f-a269-abe836d0c01e" - ], - "contains": [ - "msgoffice365 user id" - ], - "column_name": "User ID", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.jobTitle", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.mail", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.mobilePhone", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.officeLocation", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.preferredLanguage", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.surname", - "data_type": "string", - "example_values": [ - "Globaltest" - ], - "column_name": "Surname", - "column_order": 4 - }, - { - "data_path": "action_result.data.*.userPrincipalName", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "msgoffice365 user principal name", - "email" - ], - "column_name": "User Principal Name", - "column_order": 1 - }, - { - "data_path": "action_result.summary.total_users_returned", - "data_type": "numeric", - "example_values": [ - 11 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved 11 users" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "List Users", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "list groups", - "identifier": "list_groups", - "description": "List all the groups in an organization, including but not limited to Office 365 groups", - "type": "investigate", - "read_only": true, - "parameters": { - "filter": { - "description": "Search for specific results", - "data_type": "string", - "order": 0 - }, - "limit": { - "description": "Maximum number of groups to return", - "data_type": "numeric", - "order": 1 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.filter", - "data_type": "string", - "example_values": [ - "displayName eq 'Group Name'" - ] - }, - { - "data_path": "action_result.parameter.limit", - "data_type": "numeric", - "example_values": [ - 20 - ] - }, - { - "data_path": "action_result.data.*.classification", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2018-09-11T09:51:07Z" - ] - }, - { - "data_path": "action_result.data.*.creationOptions", - "data_type": "string", - "example_values": [ - "ExchangeProvisioningFlags:3552" - ] - }, - { - "data_path": "action_result.data.*.deletedDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.description", - "data_type": "string", - "example_values": [ - "This is for testing purpose" - ], - "column_name": "Description", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "example_values": [ - "Test-test-site" - ], - "column_name": "Display Name", - "column_order": 2 - }, - { - "data_path": "action_result.data.*.expirationDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.groupTypes", - "data_type": "string", - "example_values": [ - "Unified" - ], - "column_name": "Group Type", - "column_order": 4 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "2a201c95-101b-42d9-a7af-9a2fdf8193f1" - ], - "contains": [ - "msgoffice365 group id" - ], - "column_name": "Group ID", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.isAssignableToRole", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.mail", - "data_type": "string", - "example_values": [ - "Test-test-site@testdomain.abc.com" - ], - "contains": [ - "email", - "msgoffice365 group e-mail address", - "msgoffice365 group email address" - ], - "column_name": "Group Mail ID", - "column_order": 1 - }, - { - "data_path": "action_result.data.*.mailEnabled", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.mailNickname", - "data_type": "string", - "example_values": [ - "Test-test-site" - ] - }, - { - "data_path": "action_result.data.*.membershipRule", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.membershipRuleProcessingState", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onPremisesDomainName", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onPremisesLastSyncDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onPremisesNetBiosName", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onPremisesSamAccountName", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onPremisesSecurityIdentifier", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.onPremisesSyncEnabled", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.preferredDataLocation", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.preferredLanguage", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.proxyAddresses", - "data_type": "string", - "example_values": [ - "SMTP:test-h@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.renewedDateTime", - "data_type": "string", - "example_values": [ - "2018-09-11T09:51:07Z" - ] - }, - { - "data_path": "action_result.data.*.resourceBehaviorOptions", - "data_type": "string", - "example_values": [ - "WelcomeEmailDisabled" - ] - }, - { - "data_path": "action_result.data.*.resourceProvisioningOptions", - "data_type": "string", - "example_values": [ - "Team" - ] - }, - { - "data_path": "action_result.data.*.securityEnabled", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.securityIdentifier", - "data_type": "string", - "example_values": [ - "S-1-12-1-294681889-1319597617-672379543-28952022" - ] - }, - { - "data_path": "action_result.data.*.theme", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.visibility", - "data_type": "string", - "example_values": [ - "Private" - ], - "column_name": "Visibility", - "column_order": 5 - }, - { - "data_path": "action_result.summary.total_groups_returned", - "data_type": "numeric", - "example_values": [ - 9 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved 9 groups" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "List Groups", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "list group members", - "identifier": "list_group_members", - "description": "List all the members in group by group ID or group e-mail address", - "type": "investigate", - "read_only": true, - "parameters": { - "method": { - "description": "Method to use to list group members", - "data_type": "string", - "required": true, - "value_list": [ - "Group ID", - "Group e-mail" - ], - "default": "Group ID", - "order": 0 - }, - "identificator": { - "description": "Group ID or group e-mail address, based on the selected method", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 group id", - "msgoffice365 group email address", - "msgoffice365 group e-mail address" - ], - "order": 1 - }, - "get_transitive_members": { - "description": "Get a list of the group's members. A group can have users, devices, organizational contacts, and other groups as members. This operation is transitive and returns a flat list of all nested members", - "data_type": "boolean", - "default": true, - "order": 2 - }, - "filter": { - "description": "Search for specific results", - "data_type": "string", - "order": 3 - }, - "limit": { - "description": "Maximum number of members to return", - "data_type": "numeric", - "order": 4 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.filter", - "data_type": "string", - "example_values": [ - "displayName eq 'Group Name'" - ] - }, - { - "data_path": "action_result.parameter.get_transitive_members", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.method", - "data_type": "string", - "example_values": [ - "Group ID", - "Group e-mail" - ] - }, - { - "data_path": "action_result.parameter.identificator", - "data_type": "string", - "contains": [ - "msgoffice365 group id", - "msgoffice365 group email address", - "msgoffice365 group e-mail address" - ], - "example_values": [ - "TEST7d21-7631-4ea7-97b2-1328d1c5b901", - "example@test.com" - ], - "column_name": "Group ID or Group e-mail", - "column_order": 0 - }, - { - "data_path": "action_result.parameter.limit", - "data_type": "numeric", - "example_values": [ - 20 - ] - }, - { - "data_path": "action_result.data.*.@odata.type", - "data_type": "string", - "example_values": [ - "#test.abc.user" - ] - }, - { - "data_path": "action_result.data.*.businessPhones", - "data_type": "string", - "example_values": [ - "2056120271" - ] - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "example_values": [ - "Test Admin" - ], - "column_name": "Display Name", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.givenName", - "data_type": "string", - "example_values": [ - "Test" - ], - "column_name": "Given Name", - "column_order": 4 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "6132ca31-7a09-434f-a269-abe836d0c01e" - ], - "contains": [ - "msgoffice365 user id" - ], - "column_name": "User ID", - "column_order": 1 - }, - { - "data_path": "action_result.data.*.jobTitle", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.mail", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.mobilePhone", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.officeLocation", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.preferredLanguage", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.surname", - "data_type": "string", - "example_values": [ - "Globaltest" - ], - "column_name": "Surname", - "column_order": 5 - }, - { - "data_path": "action_result.data.*.userPrincipalName", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "msgoffice365 user principal name", - "email" - ], - "column_name": "User Principal Name", - "column_order": 2 - }, - { - "data_path": "action_result.summary.total_members_returned", - "data_type": "numeric", - "example_values": [ - 9 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved 9 groups" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "List Group Members", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "list folders", - "identifier": "list_folders", - "description": "Retrieve a list of mail folders", - "verbose": "If you want to list all the child folders (includes all the sub-levels) of the specific parent folder, then, you have to provide the parent folder_id parameter. If you don't provide folder_id it will list all the folders on Office 365 account (includes all the sub-level folders).", - "type": "investigate", - "read_only": true, - "parameters": { - "user_id": { - "description": "User ID/Principal name", - "data_type": "string", - "required": true, - "primary": true, - "order": 0, - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ] - }, - "folder_id": { - "description": "Parent mail folder ID", - "data_type": "string", - "primary": true, - "order": 1, - "contains": [ - "msgoffice365 folder id" - ] - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.folder_id", - "data_type": "string", - "example_values": [ - "AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAD9nLiRAAA=" - ], - "contains": [ - "msgoffice365 folder id" - ] - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "msgoffice365 user id", - "msgoffice365 user principal name", - "email" - ] - }, - { - "data_path": "action_result.data.*.childFolderCount", - "data_type": "numeric", - "example_values": [ - 1 - ], - "column_name": "Child Folder Count", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "example_values": [ - "test" - ], - "column_name": "Display Name", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAD9nLiRAAA=" - ], - "column_name": "Folder ID", - "column_order": 1, - "contains": [ - "msgoffice365 folder id" - ] - }, - { - "data_path": "action_result.data.*.isHidden", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "example_values": [ - "AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAAAAAEIAAA=" - ], - "column_name": "Parent Folder ID", - "column_order": 2, - "contains": [ - "msgoffice365 folder id" - ] - }, - { - "data_path": "action_result.data.*.sizeInBytes", - "data_type": "numeric", - "example_values": [ - 7920 - ] - }, - { - "data_path": "action_result.data.*.totalItemCount", - "data_type": "numeric", - "example_values": [ - 0 - ], - "column_name": "Total Item Count", - "column_order": 5 - }, - { - "data_path": "action_result.data.*.unreadItemCount", - "data_type": "numeric", - "example_values": [ - 0 - ], - "column_name": "Unread Item Count", - "column_order": 4 - }, - { - "data_path": "action_result.summary.total_folders_returned", - "data_type": "numeric", - "example_values": [ - 14 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully retrieved 14 mail folders" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "List Folders", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "copy email", - "identifier": "copy_email", - "description": "Copy an email to a folder", - "verbose": "The get_folder_id parameter should be enabled only when you have specified folder name/folder path in the folder parameter. If you provide folder ID in the folder parameter and set get_folder_id parameter to true, it will throw an error of folder ID not found for given folder name (because the action considers folder parameter value as folder name/folder path). The folder parameter must be either a (case sensitive) well-known name [list here; https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0] or the internal o365 folder ID. The action supports copying to a folder that is nested within another. To copy in such a folder, specify the complete folder path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate.", - "type": "generic", - "read_only": false, - "parameters": { - "id": { - "description": "Message ID to copy", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 message id" - ], - "order": 0 - }, - "email_address": { - "description": "Source mailbox (email)", - "data_type": "string", - "required": true, - "contains": [ - "email" - ], - "order": 1, - "primary": true - }, - "folder": { - "description": "Destination folder; this must be either a (case-sensitive) well-known name or the internal o365 folder ID", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path", - "msgoffice365 folder id" - ], - "order": 2 - }, - "get_folder_id": { - "description": "Assume the folder parameter contains a folder name/folder path, separated by '/' ; i.e. Inbox/dir1/dir2/dir3. If this parameter is enabled, it retrieves the folder ID for the provided folder name/folder path automatically and replaces the parameter value", - "data_type": "boolean", - "default": true, - "order": 3 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "column_name": "Email", - "column_order": 0, - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.folder", - "data_type": "string", - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path", - "msgoffice365 folder id" - ], - "column_name": "Folder", - "column_order": 1, - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA" - ] - }, - { - "data_path": "action_result.parameter.get_folder_id", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.id", - "data_type": "string", - "contains": [ - "msgoffice365 message id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpdwAAAA==" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/$metadata#message" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9\"" - ] - }, - { - "data_path": "action_result.data.*.@odata.type", - "data_type": "string", - "example_values": [ - "#test.abc.message" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`plain text?\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "text" - ] - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string", - "example_values": [ - "plain text?" - ] - }, - { - "data_path": "action_result.data.*.categories", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.ccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.ccRecipients.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string", - "example_values": [ - "CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9" - ] - }, - { - "data_path": "action_result.data.*.conversationId", - "data_type": "string", - "example_values": [ - "AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQANDpL7xEHORGgd1idbVXqcg=" - ] - }, - { - "data_path": "action_result.data.*.conversationIndex", - "data_type": "string", - "example_values": [ - "AQHW+IHb9hH4JnJtjUmniPjyy9YF2Y==" - ] - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2017-10-25T22:29:01Z" - ] - }, - { - "data_path": "action_result.data.*.flag.flagStatus", - "data_type": "string", - "example_values": [ - "notFlagged" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "column_name": "New Message ID", - "column_order": 2, - "contains": [ - "msgoffice365 message id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAAQSl1b8BFiEmbqZql_JiUtwABUH-stgAAAA==" - ] - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.inferenceClassification", - "data_type": "string", - "example_values": [ - "focused" - ] - }, - { - "data_path": "action_result.data.*.internetMessageId", - "data_type": "string", - "example_values": [ - "" - ], - "contains": [ - "msgoffice365 internet message id" - ], - "column_name": "Internet Message ID", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.isDeliveryReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isRead", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2017-11-02T23:58:59Z" - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA" - ] - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "example_values": [ - "2017-10-25T22:29:01Z" - ] - }, - { - "data_path": "action_result.data.*.replyTo", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.sentDateTime", - "data_type": "string", - "example_values": [ - "2017-10-25T22:28:57Z" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "example_values": [ - "more body formats?" - ], - "contains": [ - "msgoffice365 subject" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "Test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "example_values": [ - "https://outlook.office365.com/owa/?ItemID=AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0%2BMMHKonvdoWQcAQSl1b8BFiEmbqZql%2BJiUtwAAAgEbAAAAQSl1b8BFiEmbqZql%2BJiUtwABUH%2FstgAAAA%3D%3D&exvsurl=1&viewmodel=ReadMessageItem" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully copied email" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Copy Email", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "move email", - "identifier": "move_email", - "description": "Move an email to a folder", - "verbose": "The get_folder_id parameter should be enabled only when you have specified folder name/folder path in the folder parameter. If you provide folder ID in the folder parameter and set get_folder_id parameter to true, it will throw an error of folder ID not found for given folder name (because the action considers folder parameter value as folder name/folder path). The folder parameter must be either a (case sensitive) well-known name [list here; https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0] or the internal o365 folder ID. The action supports moving to a folder that is nested within another. To copy in such a folder, specify the complete folder path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate.", - "type": "generic", - "read_only": false, - "parameters": { - "id": { - "description": "Message ID to move", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 message id" - ], - "order": 0 - }, - "email_address": { - "description": "Source mailbox (email)", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 1 - }, - "folder": { - "description": "Destination folder; this must be either a (case-sensitive) well-known name or the internal o365 folder ID", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path", - "msgoffice365 folder id" - ], - "order": 2 - }, - "get_folder_id": { - "description": "Assume the folder parameter contains a folder name/folder path, separated by '/'(forward slash) ; i.e. Inbox/dir1/dir2/dir3. If this parameter is enabled, it retrieves the folder ID for the provided folder name/folder path automatically and replaces the parameter value", - "data_type": "boolean", - "default": true, - "order": 3 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "column_name": "Email", - "column_order": 0, - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.folder", - "data_type": "string", - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path", - "msgoffice365 folder id" - ], - "column_name": "Folder", - "column_order": 1, - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA" - ] - }, - { - "data_path": "action_result.parameter.get_folder_id", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.id", - "data_type": "string", - "contains": [ - "msgoffice365 message id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpdwAAAA==" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/$metadata#message" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9\"" - ] - }, - { - "data_path": "action_result.data.*.@odata.type", - "data_type": "string", - "example_values": [ - "#test.abc.message" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.name", - "data_type": "string", - "example_values": [ - "Test User" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`plain text?\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "text" - ] - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string", - "example_values": [ - "plain text?" - ] - }, - { - "data_path": "action_result.data.*.categories", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.ccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.ccRecipients.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string", - "example_values": [ - "CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9" - ] - }, - { - "data_path": "action_result.data.*.conversationId", - "data_type": "string", - "example_values": [ - "AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQANDpL7xEHORGgd1idbVXqcg=" - ] - }, - { - "data_path": "action_result.data.*.conversationIndex", - "data_type": "string", - "example_values": [ - "AQHW+IHb9hH4JnJtjUmniPjyy9YF1Y==" - ] - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2017-10-25T22:29:01Z" - ] - }, - { - "data_path": "action_result.data.*.flag.flagStatus", - "data_type": "string", - "example_values": [ - "notFlagged" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "column_name": "New Message ID", - "column_order": 2, - "contains": [ - "msgoffice365 message id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAAQSl1b8BFiEmbqZql_JiUtwABUH-stgAAAA==" - ] - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.inferenceClassification", - "data_type": "string", - "example_values": [ - "focused" - ] - }, - { - "data_path": "action_result.data.*.internetMessageId", - "data_type": "string", - "example_values": [ - "" - ], - "column_name": "Internet Message ID", - "column_order": 3, - "contains": [ - "msgoffice365 internet message id" - ] - }, - { - "data_path": "action_result.data.*.isDeliveryReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isRead", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2017-11-02T23:58:59Z" - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEbAAAA" - ] - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "example_values": [ - "2017-10-25T22:29:01Z" - ] - }, - { - "data_path": "action_result.data.*.replyTo", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.sentDateTime", - "data_type": "string", - "example_values": [ - "2017-10-25T22:28:57Z" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "example_values": [ - "more body formats?" - ], - "contains": [ - "msgoffice365 subject" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "Test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "example_values": [ - "https://outlook.office365.com/owa/?ItemID=AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0%2BMMHKonvdoWQcAQSl1b8BFiEmbqZql%2BJiUtwAAAgEbAAAAQSl1b8BFiEmbqZql%2BJiUtwABUH%2FstgAAAA%3D%3D&exvsurl=1&viewmodel=ReadMessageItem" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully moved email" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Move Email", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "delete email", - "identifier": "delete_email", - "description": "Delete an email", - "type": "contain", - "read_only": false, - "parameters": { - "id": { - "description": "Message ID to delete", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 message id" - ], - "order": 0 - }, - "email_address": { - "description": "Email address of the mailbox owner", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 1 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "column_name": "Status", - "column_order": 2, - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "column_name": "Email", - "column_order": 1, - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.id", - "data_type": "string", - "contains": [ - "msgoffice365 message id" - ], - "column_name": "Message ID", - "column_order": 0, - "example_values": [ - "AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAEMAABvUzMoUJx2S4nbgxzZWx2PAAEIbt7NAAA=" - ] - }, - { - "data_path": "action_result.data", - "data_type": "string" - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully deleted email" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Delete email", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "delete event", - "identifier": "delete_event", - "description": "Delete an event from user calendar", - "type": "generic", - "read_only": false, - "parameters": { - "id": { - "description": "Event ID to delete", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 event id" - ], - "order": 0 - }, - "email_address": { - "description": "Email address of the mailbox owner", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 1 - }, - "send_decline_response": { - "description": "Send decline response to the organizer", - "data_type": "boolean", - "default": true, - "order": 2 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "column_name": "Status", - "column_order": 2, - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "column_name": "Email", - "column_order": 1, - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.id", - "data_type": "string", - "contains": [ - "msgoffice365 event id" - ], - "column_name": "Event ID", - "column_order": 0, - "example_values": [ - "TestAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwBGAAAAAADRlY7ewL4xToKRDciQog5UBwBvUzMoUJx2S4nbgxzZWx2PAAAAAAEMAABvUzMoUJx2S4nbgxzZWx2PAAEIbt7NAAA=" - ] - }, - { - "data_path": "action_result.parameter.send_decline_response", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data", - "data_type": "string" - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully deleted email" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Delete email", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "get email", - "identifier": "get_email", - "description": "Get an email from the server", - "verbose": "If the 'download attachments' parameter is set to true, the action will ingest the '#microsoft.graph.itemAttachment' and '#microsoft.graph.fileAttachment' type of attachments.", - "type": "investigate", - "read_only": true, - "parameters": { - "id": { - "description": "Message ID to get", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 message id" - ], - "order": 0 - }, - "email_address": { - "description": "Email address of the mailbox owner", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 1 - }, - "download_attachments": { - "description": "Download attachments to vault", - "data_type": "boolean", - "order": 2 - }, - "extract_headers": { - "description": "Extract email headers", - "data_type": "boolean", - "order": 3 - }, - "download_email": { - "description": "Download email to vault", - "data_type": "boolean", - "order": 4 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.download_attachments", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.download_email", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@abc.com" - ] - }, - { - "data_path": "action_result.parameter.extract_headers", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.id", - "data_type": "string", - "contains": [ - "msgoffice365 message id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpfAAAAA==" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://abc.test.com/v1.0/$metadata#users('test%40abc.com')/messages/$entity" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFLoCsS\"" - ] - }, - { - "data_path": "action_result.data.*.@odata.type", - "data_type": "string", - "example_values": [ - "#test.abc.eventMessage" - ] - }, - { - "data_path": "action_result.data.*.allowNewTimeProposals", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.attachments.*.@odata.mediaContentType", - "data_type": "string", - "example_values": [ - "application/octet-stream" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.@odata.type", - "data_type": "string", - "example_values": [ - "#test.abc.fileAttachment" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.attachmentType", - "data_type": "string", - "example_values": [ - "#test.abc.fileAttachment" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.contentId", - "data_type": "string", - "example_values": [ - "F5832F4CF6EFEC41B9CBC6DED238A234@namprd18.prod.test.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.contentLocation", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.attachments.*.contentType", - "data_type": "string", - "example_values": [ - "text/plain" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.id", - "data_type": "string", - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpfAAAAAESABAA0EuDIoiFb0ifXM0ETmQMVw==" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.isInline", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.attachments.*.itemType", - "data_type": "string", - "example_values": [ - "#test.abc.message" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2017-10-26T01:31:43Z" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.name", - "data_type": "string", - "example_values": [ - "attachment.txt" - ] - }, - { - "data_path": "action_result.data.*.attachments.*.size", - "data_type": "numeric", - "example_values": [ - 355 - ] - }, - { - "data_path": "action_result.data.*.attachments.*.vaultId", - "data_type": "string", - "example_values": [ - "719dbf72d7c0bc89d7e34306c08a0b66191902b9" - ], - "contains": [ - "sha1", - "vault id" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`Have a good time with these.\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "text" - ] - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string", - "example_values": [ - "Have a good time with these." - ] - }, - { - "data_path": "action_result.data.*.categories", - "data_type": "string", - "example_values": [ - "Green category" - ] - }, - { - "data_path": "action_result.data.*.ccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.ccRecipients.name", - "data_type": "string", - "example_values": [ - "Test Domain" - ] - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string", - "example_values": [ - "CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFLoCsS" - ] - }, - { - "data_path": "action_result.data.*.conversationId", - "data_type": "string", - "example_values": [ - "AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQAEU43FQ-fk5LmOxKoTpmHfw=" - ] - }, - { - "data_path": "action_result.data.*.conversationIndex", - "data_type": "string", - "example_values": [ - "AQHWRVB0TS7xy6ZOSEeEl0ahrRHNfQ==" - ] - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2017-10-26T01:31:43Z" - ] - }, - { - "data_path": "action_result.data.*.endDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2022-07-26T09:30:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.endDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.event.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFLoCsS\"" - ] - }, - { - "data_path": "action_result.data.*.event.allowNewTimeProposals", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.attendees.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.event.attendees.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.event.attendees.*.status.response", - "data_type": "string", - "example_values": [ - "none" - ] - }, - { - "data_path": "action_result.data.*.event.attendees.*.status.time", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00Z" - ] - }, - { - "data_path": "action_result.data.*.event.attendees.*.type", - "data_type": "string", - "example_values": [ - "required" - ] - }, - { - "data_path": "action_result.data.*.event.body.content", - "data_type": "string", - "example_values": [ - "`plain text?\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.event.body.contentType", - "data_type": "string", - "example_values": [ - "text" - ] - }, - { - "data_path": "action_result.data.*.event.bodyPreview", - "data_type": "string", - "example_values": [ - "plain text?" - ] - }, - { - "data_path": "action_result.data.*.event.calendar@odata.associationLink", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/users('test@user.abc.com')/calendars('EXAMPLECALENDERID')/$ref" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.event.calendar@odata.navigationLink", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/users('test@user.abc.com')/calendars('EXAMPLECALENDERID')" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.event.changeKey", - "data_type": "string", - "example_values": [ - "CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFQwHj9" - ] - }, - { - "data_path": "action_result.data.*.event.createdDateTime", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00Z" - ] - }, - { - "data_path": "action_result.data.*.event.end.dateTime", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.event.end.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.event.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.hideAttendees", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.iCalUId", - "data_type": "string", - "example_values": [ - "040000008200E00074C5B7101A82E0080000000074AC2CF9CF5ED8010000000000000000100000005B5B104FC55A6E44BC5C6A093AB7F07C" - ] - }, - { - "data_path": "action_result.data.*.event.id", - "data_type": "string", - "example_values": [ - "AAMkAGYwYmE5NmQ0LWVhOGItNGFhMy05OWNlLTE5MzhjMTE5YWYyMQBGAAAAAACkbmmUA5U5RZwZvvg8zew_BwDWyBbuDx-uTKpJ-DXRPByGAAAA1bxhAAAr0tXr3dtaS5qYgFGhi6QjAAQiOoS2AAA=" - ], - "contains": [ - "msgoffice365 event id" - ] - }, - { - "data_path": "action_result.data.*.event.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.event.isAllDay", - "data_type": "boolean", - "example_values": [ - false, - true - ] - }, - { - "data_path": "action_result.data.*.event.isCancelled", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.isOnlineMeeting", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.isOrganizer", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.isReminderOn", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00Z" - ] - }, - { - "data_path": "action_result.data.*.event.location.displayName", - "data_type": "string", - "example_values": [ - "Test" - ] - }, - { - "data_path": "action_result.data.*.event.location.locationType", - "data_type": "string", - "example_values": [ - "default" - ] - }, - { - "data_path": "action_result.data.*.event.location.uniqueIdType", - "data_type": "string", - "example_values": [ - "unknown" - ] - }, - { - "data_path": "action_result.data.*.event.occurrenceId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.event.onlineMeeting.joinUrl", - "data_type": "string", - "example_values": [ - "https://test.abc.com/l/meetup-join/19%3ameeting_ZjViMTdlNjEtZjYxNi00N2QyLWJmOWYtMGU5MjVjMDM3ZTZl%40thread.v2/0?context=%7b%22Tid%22%3a%22a417c578-c7ee-480d-a225-d48057e74df5%22%2c%22Oid%22%3a%22e4c722ac-3b83-478d-8f52-c388885dc30f%22%7d" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.event.onlineMeetingProvider", - "data_type": "string", - "example_values": [ - "teamsForBusiness" - ] - }, - { - "data_path": "action_result.data.*.event.onlineMeetingUrl", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.event.organizer.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.event.organizer.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.event.originalEndTimeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.event.originalStartTimeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.event.recurrence", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.event.reminderMinutesBeforeStart", - "data_type": "numeric", - "example_values": [ - 0 - ] - }, - { - "data_path": "action_result.data.*.event.responseRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.event.responseStatus.response", - "data_type": "string", - "example_values": [ - "accepted" - ] - }, - { - "data_path": "action_result.data.*.event.responseStatus.time", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00Z" - ] - }, - { - "data_path": "action_result.data.*.event.sensitivity", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.event.seriesMasterId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.event.showAs", - "data_type": "string", - "example_values": [ - "tentative" - ] - }, - { - "data_path": "action_result.data.*.event.start.dateTime", - "data_type": "string", - "example_values": [ - "0001-01-01T00:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.event.start.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.event.subject", - "data_type": "string", - "example_values": [ - "Just wanted to say hello" - ] - }, - { - "data_path": "action_result.data.*.event.transactionId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.event.type", - "data_type": "string", - "example_values": [ - "singleInstance" - ] - }, - { - "data_path": "action_result.data.*.event.webLink", - "data_type": "string", - "example_values": [ - "https://test.abc.com/owa?itemid=AAMkAGYwYmE5NmQ0LWVhOGItNGFhMy05OWNlLTE5MzhjMTE5YWYyMQBGAAAAAACkbmmUA5U5RZwZvvg8zew%2BBwDWyBbuDx%2FuTKpJ%2FDXRPByGAAAA1bxhAAAr0tXr3dtaS5qYgFGhi6QjAAQiOoS2AAA%3D&exvsurl=1&path=/calendar/item" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.flag.flagStatus", - "data_type": "string", - "example_values": [ - "notFlagged" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwABS2DpfAAAAA==" - ], - "contains": [ - "msgoffice365 message id" - ] - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.inferenceClassification", - "data_type": "string", - "example_values": [ - "focused" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.ARC-Authentication-Results", - "data_type": "string", - "example_values": [ - "i=1; mx.test.com 1; spf=pass smtp.mailfrom=user.test.com; dmarc=pass action=none header.from=user.abc.com; dkim=pass header.d=test.abc.com; arc=none" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.ARC-Message-Signature", - "data_type": "string", - "example_values": [ - "i=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=3fZ/7Rn8nzby4BCiwxGpfUUiHgfvSZ6MOHtHg1Hmixs=; b=f7VAkSO/T2NJuxMrJNBzeow/t2iTVZfAkVaZGgtAoYq3Wb9cUDEfAtTE4RmzSqLEqP2iXxLK7JsRfV2uqsbGinp3ZuRnHSqMoRzO0XCN8KjE/Z9hUyUmq05clk2rB3TqCcKK6ipy1+N+4mvCFFb6O+bN/9TGhPos1dY0X0sy33sow9oeND6nm8HvtIzp/hG0xKduPnEHwQiCCwRTmdAtbAzMWmnEyyodRQg/PCgjAPKPpeFELZ+pr/bbEkySxLCu/xY1qhoa8JMIrh1EUFHyFyscTDc580YNnowNqAR//iO8DRGaTG1Znv4MI7mlcmM/vopKYAa6zNPXb2LHDqwKBQ==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.ARC-Seal", - "data_type": "string", - "example_values": [ - "i=1; a=rsa-sha256; s=arcselector9901; d=test.com; cv=none; b=GIfa87IhNn+v4Mdn75nJpk9WjELaP8fw9+C+Ey0QFktfU60rPvtJDc5qJS5mN/g6COFa2inOEc4S+Cm3R1BLcBgqkCZR/niPjMp/A2tEHs9OeY75S+T8d5OsfmFcD2jC5/59Dc8EAEKw3UnYKxCC4CKWPFCXrE+Cu3jLEtt04Izr1rXTTQacfCjwYN4OsKI9lHyn6JRDlePR2RZKztMcnt/hcOJz6cwFe+MAUgg0qjKB8p+27o2hQKu+LlQDg8nMRQ6jHkD8DylOclHe2nVKmEhNXi67PTeoqTfZe4+YgSOrnJOUdlra5q/EoWN0FnM9Zt0+0K42ncYSWcC0NpsbqQ==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Accept-Language", - "data_type": "string", - "example_values": [ - "en-US" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Authentication-Results", - "data_type": "string", - "example_values": [ - "spf=pass (sender IP is 209.85.210.171) smtp.mailfrom=testdomain.com; .abc.com; dkim=pass (signature was verified) header.d=testdomain.com.20150623.gappssmtp.com;.abc.com; dmarc=pass action=none header.from=testdomain.com;compauth=pass reason=100" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Authentication-Results-Original", - "data_type": "string", - "example_values": [ - "dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=test.abc.com;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Language", - "data_type": "string", - "example_values": [ - "en-US" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Transfer-Encoding", - "data_type": "string", - "example_values": [ - "binary" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Type", - "data_type": "string", - "example_values": [ - "multipart/related" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.DKIM-Signature", - "data_type": "string", - "example_values": [ - "v=1; a=rsa-sha256; c=relaxed/relaxed; d=testdomain.com.20150623.gappssmtp.com; s=20150623; h=message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=avAAeJ8jF08K4oIBhxTirRmyB+SXHwdU0zdxv7eqs/zWaWWcgmT0007KP560TTgo5u oD4nb6TvKxpRyWW4QwmkbuMIwHsMvehd2l1gispV3AawyGJjpmN7ErVYfLtIkz2Tap3V YxmluV+SqeyyxTU8pFAEZ7+2C2lOb1DO5TC7xCMv+dyzevSscJdbeN0dFkG+C93zCqkg w2fxubx2HDD7b/U6m2wXllYhH608wKJ/qYzyvQyqxYqNiQOtPRg2gw4sZ2UgN3+UQyVq 8ubO39ZuqakJpzEzYMw10d6E7SQhvHDJH7mFwhBlzhvOpb2gLJDN8n8dJaZo05BozQqq MsvA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Date", - "data_type": "string", - "example_values": [ - "Thu, 18 Jun 2020 02:11:26 -0700" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.From", - "data_type": "string", - "example_values": [ - "\"Test\" " - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.In-Reply-To", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.MIME-Version", - "data_type": "string", - "example_values": [ - "1.0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Message-ID", - "data_type": "string", - "example_values": [ - "<5eeb2fbe.1c69fb81.22b4b.676a@mx.test.com>" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Received", - "data_type": "string", - "example_values": [ - "from localhost.localdomain (host-240.test.com. [204.107.141.240]) by test.abc.com with UTF8SMTPSA id ng12sm1923252pjb.15.2020.06.18.02.11.26 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jun 2020 02:11:26 -0700 (PDT)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Received-SPF", - "data_type": "string", - "example_values": [ - "Pass (protection.test.com: domain of testdomain.com designates 209.85.210.171 as permitted sender) receiver=protection.test.com; client-ip=209.85.210.171; helo=mail-pf1-f171.test.com;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.References", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Return-Path", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Subject", - "data_type": "string", - "example_values": [ - "Fw: Email having different attachments" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Thread-Index", - "data_type": "string", - "example_values": [ - "AQDEZLqyXR4k4Sc6skyFCMPITcMsbKpGS7At" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Thread-Topic", - "data_type": "string", - "example_values": [ - "Email having different attachments" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.To", - "data_type": "string", - "example_values": [ - "\"Test\" " - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-EOPAttributedMessage", - "data_type": "string", - "example_values": [ - "0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-EOPTenantAttributedMessage", - "data_type": "string", - "example_values": [ - "a417c578-c7ee-480d-a225-d48057e74df5:0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Forefront-Antispam-Report", - "data_type": "string", - "example_values": [ - "CIP:209.85.210.171;CTRY:US;LANG:en;SCL:-1;SRV:;IPV:NLI;SFV:SFE;H:mail-pf1-f171.test.com;PTR:mail-pf1-f171.test.com;CAT:NONE;SFTY:;SFS:;DIR:INB;SFP:;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Forefront-Antispam-Report-Untrusted", - "data_type": "string", - "example_values": [ - "CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BYAPR18MB2408.namprd18.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230016)(136003)(396003)(346002)(39830400003)(376002)(366004)(186003)(83380400001)(64756008)(76116006)(66946007)(71200400001)(8676002)(66476007)(66446008)(66556008)(6916009)(55016003)(91956017)(9686003)(41300700001)(6506007)(7696005)(26005)(2906002)(33656002)(38070700005)(86362001)(38100700002)(3480700007)(122000001)(5660300002)(4744005)(316002)(296002)(166002)(478600001)(8936002)(52536014);DIR:OUT;SFP:1102;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Gm-Message-State", - "data_type": "string", - "example_values": [ - "AOAM533ynFERIhSIewEEkj4b8B1rPNOEeie1IxBdrd55treEMtBa1jkL\tcO5ee4Ff6p0FYedfFtVtHKiCglGTpFTOSw==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Google-DKIM-Signature", - "data_type": "string", - "example_values": [ - "v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=fPT47NIiheeY6GM0bxUOlsmnOgN4WuiOlalFvZqrAiFiOoYk6zrznvgIcAtiHZ4nxE naQAa+mZs5svqRjib3YI52OvR5U8MitIYaa0Rt3LyYSUO1s3iKTUs4nHyRnqPt1skNl7 2OUwsZPXo3ShJDw/uxZRu/cuN1iIfeuE02PrbR04p4D8+1XRslqt/Xqm/bOWKUauqZWe dH1E7meFY01hXxODreO4nWHIhsZgr49TpP/OqRyFcyKHHFFg2sPGXz+QNah6jP4YQUYd Tty2wzOX3nc/YS7TkVo3ORmbzh9o+UZaqH8wHbQlyTdklYxoMPvJwZTo72rTxZeqiJ9E J7PQ==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Google-Smtp-Source", - "data_type": "string", - "example_values": [ - "ABdhPJxrYC7raBubCCIOmauxmxryzS9KsihTN6XCRgaNp2rDrG71TVxryzYCtelFOZ2Xj1LzcYIiMA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData", - "data_type": "string", - "example_values": [ - "VSM9HTzub/OH3NCwWNKQqkkzjnhdw5kXsgd9WM0SRgZ0qRdPg5D9/o3LA7lf8ziXc5k0mm9M5mHvFoYePXNXs/MGhGdBGxa/qUQ+FVHA2mDgfPkamJCEZxz//OX/uruTDo+zF4p9D1dQJpnIpx1M75OhuvrHX/BxWWzyAh78DXfF214YHdyFBCYepwl56CS7+fSGQL/r3p+OvWIBnIkISC+HJljSro2k47pPPAkspMhoUkb+zklyENFjez+JcEHYlih2FiNeUO8kb9b7qvlm3zPK98HLspzDh4BojpQ6Ff330iy7nfIK726tCMByxjOdnEQSB9Ua2sbE5gxSeeWL8MB5DHcQSSsXg+sR8w4gXrXLO3meE0lNQKRoAv2b1U0Q+yM0QBqeQWlymZG21bKeuH4gtAFQvfXNjoCtIbBQK1n7ZnL7fI21FJZRcMcKEneus6gLYUqD4PdLEq9FEGbfgiLmVYeUAL2A0Q/gectvL1OVudtHVR5gFMJKt65F1OtS04CPulfLLFSl1F4AzpjjtBSyQcK9R7bOsjoHxQXPMd9fMCzMSIq5f551pO0klKqWY7l11Un2Noj6CA7EtXiD1bTv8JmYQEKR+0HTZagNd+79GeTvKjxTvt9MkyO8k3aqWyNqT331ITnVICtksN1TVMCp8GVeDudNMr2PLSW0alOduR5unuEgTWrqHoaTGOovQx0PVjudNlpZ80ANK9hqaC/ZhLLOtNpJ3fZnjs06PzrPLGhE/IeccY1n8sYDvGm1QA9TN6JaaGPl1Pj6ecy16k0XuF/PKGHTL0M4LCpxSS6T87oFFH1zHkKtmbJp3aAI4bt3ihbQmwFb29JyMgL7ZOy+zrIwXGILh1KQGWQQv1uXXnAuqQy29HeFXs6D2hDHxHlBk5ZQ+vgRtsvRvGnq58vJ3CapjntfL3pOINUj1avLyAZxjasBWMTwaZs9JQ4ZIMekzkIk05lh9XfDSeULk2yKaH8YSCC6ENUHxSWa6pPHJfOdp9kXwOtlp09/VTTAikKy862k9ybN4bRWZB45B9Pv5scna8IX3rthIXUih8c=" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData-Original-0", - "data_type": "string", - "example_values": [ - "SC/7FB9jcHrfqMtL+8C1Sp2MBDl48qjRn/ZzFJk8+2yWs9kmQwzxXC8TA3HuU0EMts58S8uTyFbx12+qQUJ45/B5QFEfP5j1o7B5kj1Q0C9S+LAg6YptTwxi29Ei1T4mf+CpfzIeA73NzXB7pWjnmycYYlL3TAn0xJATthWspUZ+g4jZoDzNSIdAZWqoc1j4+dHA6WR+U9zZi0vODxRmplSWq2FeO8uMKqD43StpvYk6chxcaGGc5+rNRxP6RLF7OLirJ0MQ1FfZNRdNfUKBvGAF2r6HLp62apGZg0d5gJ5M7zg/1V4ygWW19cuGXyAr5b9bow1MPIOBfrDrMS3CBk2F9DfATTZ/oFwOv/hCUVs2MkWSdr/upJfPt0xoAYtXk1UjxKpV/nHgTTfQy/ijlxEsYn/HY9O6tfqvn/BRvehvPjjoouvxcZteyP1glEcajum7Op67/kV9p4Wv+U+A+31/kyrb3Tjt4GJGpTIBfLi/VjIiHUDFrpAcBz3rFqxyfKQcCqDsOMjD5CrxBj3ow7uMlQiaQoU0Dxwc4eDggbe9Af/F3YqZuvqpj2H0U0hm7+7j56WjFxewVKHLJBwkCXG6a8UdEmKBtA685mUbfpICdYaoLatC4LYOMeqsx6CeEr75vyfdqTdKOZrBKkhTluMbR0s1pvh0kID7B+HL/LKqFSnpgQidfIugk5FUSh42vsBUjrxq6LWYDSHWwYweeuZi/6vVgKQl1XS5X7JUhwNxZSzSF76pNeCIq6KGlCmfIrAnAAhfd0a0xMGy/gOJiqM8JcWjnCMA25UrRL2XrMLC4mnLpi71rATOU62rcgNyczpKI74uHEQAGtQBHfoJjuAqK1JNAK5+j1kIT9gr1F0CC2iJ+68DOI+dveK2lkGqyNYx+eKeTjJSKO4bfzQiM0oBg9eIwqtU3Wn+sSQEg74MzrMjEfaorXh7X+LdO4DwA7dNyEurnYEgmjvjJoNbcxgjhbLSkTe3LFAX7iUia79VIqgD80bxgwoCWyKiSiCKARb/krBWOCOOuF1I6v6azxPHpwsQxC6AvFlOWW7BXsFY9NcCeGvV7l+9ZnLuRjdbjrgdk1b7PRv6aaRzmVk1I3KPBCPvFasP2XhNJXGgYmv7hxiyOci1Eoq4QcXEfr9VsrOEkcl0z4HI8yHx354Lc7Peck8XFvGCe9ElyhxmEtu/ySFQ3BOakjPikOOIA4XlUj3GsUk8qfwnePHVPBnKKm0jtaqjK6tA8uZyKG8jTNgICFZ9wKmsijFMPXVY2lt5zuPnw1V31D//IYjdXpcU1cMfXgZ06vld4anBSEv3WL3zCZaIJsRhxkB7ixumw4h5yieKNJFIVhbV6Ty4Yog9JFmQbAbRfKh1b8ltoym804+hOH8n7oWVqSbtzuylg7DPCIVOcWdUNraXYgmgtXDM6vq0SsDK1j2diz4ywM2BlBSsnCyAdt1NN5gZyMfOufZ3a8SKdwTaRH3bFh6BmfLEZ8NgYbeJQX7GrVbBU5kHT9+k2g3Klchapmqv8mzl0uKvX1av2hW6ce5xo+owa9Qk/Gza4j4o74bVl8tJ/iYClX4kpHY6kek/TOA/Sxj/IvWb1QZvpgmj0LNNCrLdrLUiTDG3lodE9j+Lr8qhmgL8S78=" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData-Original-ChunkCount", - "data_type": "string", - "example_values": [ - "1" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthAs", - "data_type": "string", - "example_values": [ - "Internal" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthSource", - "data_type": "string", - "example_values": [ - "SJ0PR22MB4941.namprd11.prod.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-FromEntityHeader", - "data_type": "string", - "example_values": [ - "Internet" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Id", - "data_type": "string", - "example_values": [ - "a417c578-c7ee-480d-a225-d48057e74df5" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-MailboxType", - "data_type": "string", - "example_values": [ - "HOSTED" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Network-Message-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-OriginalArrivalTime", - "data_type": "string", - "example_values": [ - "18 Jun 2020 09:11:28.2511 (UTC)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-UserPrincipalName", - "data_type": "string", - "example_values": [ - "bs91VnpEPjrqCnvlIeymwO6ye5P9rggHggVNUPUbV/tC9uuFPVFOYg7e/Cd0MeGmSqT4AlLW0Nn4ZeEqNieSf/D1gp5iLz/YkwjXhYUSJnLRb/csQN4sRMMZsX3LUkKkwVpifaeJzoukLu8qSWn7og==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthAs", - "data_type": "string", - "example_values": [ - "Anonymous" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthMechanism", - "data_type": "string", - "example_values": [ - "04" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthSource", - "data_type": "string", - "example_values": [ - "DM6NAM11FT055.eop-nam11.prod.protection.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationInterval", - "data_type": "string", - "example_values": [ - "1:00:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationIntervalReason", - "data_type": "string", - "example_values": [ - "OriginalSubmit" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTime", - "data_type": "string", - "example_values": [ - "18 Jun 2020 09:11:28.2531 (UTC)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTimeReason", - "data_type": "string", - "example_values": [ - "OriginalSubmit" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-MessageDirectionality", - "data_type": "string", - "example_values": [ - "Incoming" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-Network-Message-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-SCL", - "data_type": "string", - "example_values": [ - "-1" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Processed-By-BccFoldering", - "data_type": "string", - "example_values": [ - "15.20.3109.017" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersPromoted", - "data_type": "string", - "example_values": [ - "DM6NAM11FT064.eop-nam11.prod.protection.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStamped", - "data_type": "string", - "example_values": [ - "BN6PR18MB1492" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStripped", - "data_type": "string", - "example_values": [ - "DM6NAM11FT064.eop-nam11.prod.protection.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-EndToEndLatency", - "data_type": "string", - "example_values": [ - "00:00:02.7417647" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Has-Attach", - "data_type": "string", - "example_values": [ - "yes" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id-Prvs", - "data_type": "string", - "example_values": [ - "0c3038e5-2c60-453b-188f-08da6ed1ea0c" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Oob-TLC-OOBClassifiers", - "data_type": "string", - "example_values": [ - "OLM:1728;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-PublicTrafficType", - "data_type": "string", - "example_values": [ - "Email" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-TNEF-Correlator", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-TrafficTypeDiagnostic", - "data_type": "string", - "example_values": [ - "BN6PR18MB1492:" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam", - "data_type": "string", - "example_values": [ - "BCL:0;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Mailbox-Delivery", - "data_type": "string", - "example_values": [ - "wl:1;pcwl:1;ucf:0;jmr:0;auth:0;dest:I;ENG:(750128)(520011016)(520004050)(702028)(944506458)(944626604);" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info", - "data_type": "string", - "example_values": [ - "La+CSxAnpzVJOXq7njrFPhIbsh0khleSwldy+W8NYDRsoyyPruPIiId4Avama7JyfzrxoExzhLk5pDn2lGPAJIpdcguiDSsDQg5T+iBCJgFeaEJXjhstECMi842/JGawB9WsiGw9Q/PpvjO5H/2fNLlQZVZW3AAQVZSsX3az4iOsv1Ggj4aYZRMKHmPAtniWOEtQD7zAEWC0jIZf613lWy3vxHfb/3+pV9X8zqPqazbyGy5Q14PICSNkKnvIw8rmeqJV8eSHhvR51Lchib6OIN4xOpLWxkSkBTt5B95RUPnpgPvgp2yLo0Q+EYRIabLDQ0kMsv+24+RnFmr9vo2gRNuFusw8iEPsVEQyhfgIWtBtsBpyvyykxcfa6lIdzQhixZH3Tlkdh1kb15wFS3Ooz3CjaWbY8jcUot5l1p08Ypsj6r7CpIo3xE6jE0x/EeUkDK3Fu/Ol0pOsJ1N5W4iJLdjqSQM3l/t9QWlcPhD8s6D7D7JM5OUHCeFEPr7sSL+P/5zTgBaeUvwtZrlQSH2GHc+5gPW8rkwlwJLJftVEid0gO2PUOrzItzME5PXYAcdx++sF3XC1YMPLet/jMpX8T7/z7+hxFxNyifgmGJ+DkNOec7yGkkcLBz6iCaHx7OrRGwDHIcdAtV85wCk3NEDDiKyHivQpwp/gY55W+wkLe7aqSHmFzm1rUSslx+DWz8w2EgSjJxOmf0JkoNKbTFl3FObkocR0lUUQUnETuoAXUqvpWGD5B69W9XXUM8c43ozz2oBZseheSAtkLil3tMIr/CMCMILPX/LdoErNtkmiFXCPqaLFSSeyO61oCMl6Ezndtwp22nwMPUg5ofG0kdqFuTW122umhy9C6h5BcREaLhWclSyqDoZPB9RvkRlI2kTRwuwbuFW3iOMzmVwxLIQH9K5JkxdMvC3hvNpjVgz7Q2ZnEF3xSNqeoWVQvkaIe8rQLUc8s+HMRUmSERGdfSuQJAx47g8PDs9s3rS/ThUSzIaljJPbUgXEnFg/G6h3I/yXLj2Nj2OG50snoI5jJmE4+69YmNwasdDZuYpnuQeFgu11HtsLniDthJdjEJyYC1utZNt9hgA+6JlLnm7Dxb43cSIiW8ev+3X+b2kREj2k/m8fSz7YgtoCB8AkuiVXRaH3EUiq8XCExbbWeynKRgwCZ6bzvfSiT3+cg+QQKPHFc/cgot56ta6X80tjhFodpTQNTE6V6C9QFHJ3JCVhsSzVifJAc8crI5hAcPbKFEIjinENcfpF/8reo2Yr1xFElhoX" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info-Original", - "data_type": "string", - "example_values": [ - "MTrWp0GiNTxQrZWPlJ6veyKekVjJA0FKYcYQlNCKCrjhAtIn4oDZCbWPNm21Zr7mX+CXhspXNSQSEjpIERhcHIWABmuzCF/vnMr0xwLTzuh2T0si6wgTdha+BdCpnx/kvbN+TMK3rCmD9ro6qHf/v1dTbLKJUZ3Cwfl+LoqJLCkw+T8E7/De1QBlCJrKVGfSztXncKPdvPxC2Qjw/QoPmYp7oFbqW8uwNZX1q8HmAXfLzYPeOuLDE/h4s61EeZGvIY7nBMwm6DwKdTqfmQqy3+BTMfoTaN/82aHLgfBI8V0NbiPVvbKbUU8UOusHZM5H6pzOpZFOSsS7yWz1jpIvKK5CJtNUK5XkOCLxv06yzbWib2abzwEhoLUBHYhJGnwNaIznz/C7a6vZLPe1hNxyLBv0/SyYmt1m9v0vB3TSdSH2V+Ork2OK5nz/r1KcOScedjs14GZpifgjEXiIhK4eJlTkI8LE7HnqXr+82C39t13fqV8L8oRYjGiJJE9wpGJIhMa4PQYwgP0D2cQGrVwuVK2qP01TsSE+NWOXyB8hAYn11K/wCnq4TqtwRbptF9aiw0k45eDHfvB/sBARv37sajixtW4QuFQ3eYI9bVVfllmcpnKj5VUD1mdMAnkgpOCiQSEiSI6Tlo9cEycBulSc4fmocQoev7eUTFw43FDjmRpebuNF0lmm7OcvXgS8jBJSzkbLPrrsqTj1fluowoLjQTQsSPn/LK5LdOdCt7x70/BaweDCm9VdcGgh5Mqe0Qp4GxnBSpS61OCmDYZEkCL5W1A+M7Ah9mANYucdydHU9bLxJBcoi42VIDK6oYaKs3oobr0k1M/dk/pjDT65LuHwsA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Untrusted", - "data_type": "string", - "example_values": [ - "BCL:0;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Originating-IP", - "data_type": "string", - "example_values": [ - "[5.38.181.162]" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Received", - "data_type": "string", - "example_values": [ - "by 2002:aa7:84d9:: with SMTP id x25mr2807688pfn.300.1592471487394; Thu, 18 Jun 2020 02:11:27 -0700 (PDT)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.subject", - "data_type": "string", - "example_values": [ - "test html" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.x-ms-exchange-antispam-relay", - "data_type": "string", - "example_values": [ - "0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.x-ms-exchange-calendar-series-instance-id", - "data_type": "string", - "example_values": [ - "BAAAAIIA4AB0xbcQGoLgCAAAAAAhGy3GuqDYAQAAAAAAAAAAEAAAAMqUNBGdAN9NtAV6x5ZM9JU=" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.x-ms-exchange-senderadcheck", - "data_type": "string", - "example_values": [ - "1" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.x-ms-traffictypediagnostic", - "data_type": "string", - "example_values": [ - "BYAPR18MB2408:EE_MeetingMessage|BL1PR18MB4325:EE_MeetingMessage|DM6NAM11FT064:EE_|CH0PR11MB5427:EE_" - ] - }, - { - "data_path": "action_result.data.*.internetMessageId", - "data_type": "string", - "example_values": [ - "" - ], - "contains": [ - "msgoffice365 internet message id" - ] - }, - { - "data_path": "action_result.data.*.isAllDay", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDelegated", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDeliveryReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isOutOfDate", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isRead", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2017-10-26T01:31:43Z" - ] - }, - { - "data_path": "action_result.data.*.meetingMessageType", - "data_type": "string", - "example_values": [ - "meetingRequest" - ] - }, - { - "data_path": "action_result.data.*.meetingRequestType", - "data_type": "string", - "example_values": [ - "newMeetingRequest" - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAADyW3X5P7Hb0_MMHKonvdoWQEAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAA" - ] - }, - { - "data_path": "action_result.data.*.previousEndDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.previousEndDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2022-05-09T13:30:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.previousEndDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.previousLocation", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.previousStartDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.previousStartDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2022-05-09T13:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.previousStartDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "example_values": [ - "2017-10-26T01:31:43Z" - ] - }, - { - "data_path": "action_result.data.*.recurrence", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.replyTo", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.responseRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sentDateTime", - "data_type": "string", - "example_values": [ - "2017-10-26T01:31:35Z" - ] - }, - { - "data_path": "action_result.data.*.startDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2022-07-26T09:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.startDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "example_values": [ - "more attachments" - ], - "contains": [ - "msgoffice365 subject" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "Test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.type", - "data_type": "string", - "example_values": [ - "singleInstance" - ] - }, - { - "data_path": "action_result.data.*.vaultId", - "data_type": "string", - "example_values": [ - "ff89bab9ec1e063a0f100aa7b0ac5fbc7425ab22" - ] - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "example_values": [ - "https://outlook.office365.com/owa/?ItemID=AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0%2BMMHKonvdoWQcAQSl1b8BFiEmbqZql%2BJiUtwAAAgEMAAAAQSl1b8BFiEmbqZql%2BJiUtwABS2DpfAAAAA%3D%3D&exvsurl=1&viewmodel=ReadMessageItem" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully fetched email" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Get email", - "type": "custom", - "height": 5, - "view": "office365_view.display_view" - }, - "versions": "EQ(*)" - }, - { - "action": "get email properties", - "identifier": "get_email_properties", - "description": "Get non-standard email properties from the server", - "verbose": "For a list of possible properties to retrieve, visit https://docs.microsoft.com/en-us/graph/api/message-get?view=graph-rest-1.0&tabs=http.", - "type": "investigate", - "read_only": true, - "parameters": { - "id": { - "description": "Message ID to get properties of", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 message id" - ], - "order": 0 - }, - "email_address": { - "description": "Email address of the mailbox owner", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 1 - }, - "get_headers": { - "description": "Get email headers", - "data_type": "boolean", - "order": 2 - }, - "get_body": { - "description": "Get email body", - "data_type": "boolean", - "order": 3 - }, - "get_unique_body": { - "description": "Get unique email body", - "data_type": "boolean", - "order": 4 - }, - "get_sender": { - "description": "Get email sender", - "data_type": "boolean", - "order": 5 - }, - "properties_list": { - "description": "Other properties to get (comma-separated list)", - "data_type": "string", - "order": 6 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "example_values": [ - "user@abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.parameter.get_body", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.get_headers", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.get_sender", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.get_unique_body", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.id", - "data_type": "string", - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA==" - ], - "contains": [ - "msgoffice365 message id" - ] - }, - { - "data_path": "action_result.parameter.properties_list", - "data_type": "string", - "example_values": [ - "subject,receivedDateTime" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/$metadata#users('user%40.abc.com')/messages(internetMessageHeaders,body,uniqueBody,sender,subject)/$entity" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAO8DBJl\"" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`\\r\\n

HTML heading

HTML body.`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "html" - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Accept-Language", - "data_type": "string", - "example_values": [ - "en-US" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Authentication-Results", - "data_type": "string", - "example_values": [ - "spf=pass (sender IP is 209.85.210.171) smtp.mailfrom=testdomain.com; .abc.com; dkim=pass (signature was verified) header.d=testdomain.com.20150623.gappssmtp.com;.abc.com; dmarc=pass action=none header.from=testdomain.com;compauth=pass reason=100" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Language", - "data_type": "string", - "example_values": [ - "en-US" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Transfer-Encoding", - "data_type": "string", - "example_values": [ - "binary" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Type", - "data_type": "string", - "example_values": [ - "multipart/related" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.DKIM-Signature", - "data_type": "string", - "example_values": [ - "v=1; a=rsa-sha256; c=relaxed/relaxed; d=testdomain.com.20150623.gappssmtp.com; s=20150623; h=message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=avAAeJ8jF08K4oIBhxTirRmyB+SXHwdU0zdxv7eqs/zWaWWcgmT0007KP560TTgo5u oD4nb6TvKxpRyWW4QwmkbuMIwHsMvehd2l1gispV3AawyGJjpmN7ErVYfLtIkz2Tap3V YxmluV+SqeyyxTU8pFAEZ7+2C2lOb1DO5TC7xCMv+dyzevSscJdbeN0dFkG+C93zCqkg w2fxubx2HDD7b/U6m2wXllYhH608wKJ/qYzyvQyqxYqNiQOtPRg2gw4sZ2UgN3+UQyVq 8ubO39ZuqakJpzEzYMw10d6E7SQhvHDJH7mFwhBlzhvOpb2gLJDN8n8dJaZo05BozQqq MsvA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Date", - "data_type": "string", - "example_values": [ - "Thu, 18 Jun 2020 02:11:26 -0700" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.From", - "data_type": "string", - "example_values": [ - "\"Test\" " - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.In-Reply-To", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.MIME-Version", - "data_type": "string", - "example_values": [ - "1.0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Message-ID", - "data_type": "string", - "example_values": [ - "<5eeb2fbe.1c69fb81.22b4b.676a@mx.test.com>" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Received", - "data_type": "string", - "example_values": [ - "from localhost.localdomain (host-240.test.com. [204.107.141.240]) by tset.abc.com with UTF8SMTPSA id ng12sm1923252pjb.15.2020.06.18.02.11.26 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jun 2020 02:11:26 -0700 (PDT)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Received-SPF", - "data_type": "string", - "example_values": [ - "Pass (protection.test.com: domain of testdomain.com designates 209.85.210.171 as permitted sender) receiver=protection.test.com; client-ip=209.85.210.171; helo=mail-pf1-f171.test.com;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.References", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Return-Path", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Subject", - "data_type": "string", - "example_values": [ - "Fw: Email having different attachments" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Thread-Index", - "data_type": "string", - "example_values": [ - "AQHWZLqyXR4k4Sc6skyFCMPITcMsbKpGS7Bm" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Thread-Topic", - "data_type": "string", - "example_values": [ - "Email having different attachments" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.To", - "data_type": "string", - "example_values": [ - "\"Test\" " - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-EOPAttributedMessage", - "data_type": "string", - "example_values": [ - "0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-EOPTenantAttributedMessage", - "data_type": "string", - "example_values": [ - "a417c578-c7ee-480d-a225-d48057e74df5:0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Forefront-Antispam-Report", - "data_type": "string", - "example_values": [ - "CIP:209.85.210.171;CTRY:US;LANG:en;SCL:-1;SRV:;IPV:NLI;SFV:SFE;H:mail-pf1-f171.test.com;PTR:mail-pf1-f171.test.com;CAT:NONE;SFTY:;SFS:;DIR:INB;SFP:;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Gm-Message-State", - "data_type": "string", - "example_values": [ - "AOAM533ynFERIhSIewEEkj4b8B1rPNOEeie1IxBdrd55treEMtBa1jkL\tcO5ee4Ff6p0FYedfFtVtHKiCglGTpFTOSw==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Google-DKIM-Signature", - "data_type": "string", - "example_values": [ - "v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=fPT47NIiheeY6GM0bxUOlsmnOgN4WuiOlalFvZqrAiFiOoYk6zrznvgIcAtiHZ4nxE naQAa+mZs5svqRjib3YI52OvR5U8MitIYaa0Rt3LyYSUO1s3iKTUs4nHyRnqPt1skNl7 2OUwsZPXo3ShJDw/uxZRu/cuN1iIfeuE02PrbR04p4D8+1XRslqt/Xqm/bOWKUauqZWe dH1E7meFY01hXxODreO4nWHIhsZgr49TpP/OqRyFcyKHHFFg2sPGXz+QNah6jP4YQUYd Tty2wzOX3nc/YS7TkVo3ORmbzh9o+UZaqH8wHbQlyTdklYxoMPvJwZTo72rTxZeqiJ9E J7PQ==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Google-Smtp-Source", - "data_type": "string", - "example_values": [ - "ABdhPJxrYC7raBubCCIOmauxmxryzS9KsihTN6XCRgaNp2rDrG71TVxryzYCtelFOZ2Xj1LzcYIiMA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData", - "data_type": "string", - "example_values": [ - "VSM9HTzub/OH3NCwKXEQqkkzjnhdw5kXsgd9WM0SRgZ0qRdPg5D9/o3LA7lf8ziXc5k0mm9M5mHvFoYePXNXs/MGhGdBGxa/qUQ+FVHA2mDgfPkamJCEZxz//OX/uruTDo+zF4p9D1dQJpnIpx1M75OhuvrHX/BxWWzyAh78DXfF214YHdyFBCYepwl56CS7+fSGQL/r3p+OvWIBnIkISC+HJljSro2k47pPPAkspMhoUkb+zklyENFjez+JcEHYlih2FiNeUO8kb9b7qvlm3zPK98HLspzDh4BojpQ6Ff330iy7nfIK726tCMByxjOdnEQSB9Ua2sbE5gxSeeWL8MB5DHcQSSsXg+sR8w4gXrXLO3meE0lNQKRoAv2b1U0Q+yM0QBqeQWlymZG21bKeuH4gtAFQvfXNjoCtIbBQK1n7ZnL7fI21FJZRcMcKEneus6gLYUqD4PdLEq9FEGbfgiLmVYeUAL2A0Q/gectvL1OVudtHVR5gFMJKt65F1OtS04CPulfLLFSl1F4AzpjjtBSyQcK9R7bOsjoHxQXPMd9fMCzMSIq5f551pO0klKqWY7l11Un2Noj6CA7EtXiD1bTv8JmYQEKR+0HTZagNd+79GeTvKjxTvt9MkyO8k3aqWyNqT331ITnVICtksN1TVMCp8GVeDudNMr2PLSW0alOduR5unuEgTWrqHoaTGOovQx0PVjudNlpZ80ANK9hqaC/ZhLLOtNpJ3fZnjs06PzrPLGhE/IeccY1n8sYDvGm1QA9TN6JaaGPl1Pj6ecy16k0XuF/PKGHTL0M4LCpxSS6T87oFFH1zHkKtmbJp3aAI4bt3ihbQmwFb29JyMgL7ZOy+zrIwXGILh1KQGWQQv1uXXnAuqQy29HeFXs6D2hDHxHlBk5ZQ+vgRtsvRvGnq58vJ3CapjntfL3pOINUj1avLyAZxjasBWMTwaZs9JQ4ZIMekzkIk05lh9XfDSeULk2yKaH8YSCC6ENUHxSWa6pPHJfOdp9kXwOtlp09/VTTAikKy862k9ybN4bRWZB45B9Pv5scna8IX3rthIXUih8c=" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthAs", - "data_type": "string", - "example_values": [ - "Internal" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthSource", - "data_type": "string", - "example_values": [ - "SJ0QA11MB4941.namprd11.prod.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-FromEntityHeader", - "data_type": "string", - "example_values": [ - "Internet" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Id", - "data_type": "string", - "example_values": [ - "a417c578-c7ee-480d-a225-d48057e74df5" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-MailboxType", - "data_type": "string", - "example_values": [ - "HOSTED" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Network-Message-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-OriginalArrivalTime", - "data_type": "string", - "example_values": [ - "18 Jun 2020 09:11:28.2511 (UTC)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-UserPrincipalName", - "data_type": "string", - "example_values": [ - "bs91VnpEPjrqCnvlIeymwO6ye4Q8rggHggVNUPUbV/tC9uuFPVFOYg7e/Cd0MeGmSqT4AlLW0Nn4ZeEqNieSf/D1gp5iLz/YkwjXhYUSJnLRb/csQN4sRMMZsX3LUkKkwVpifaeJzoukLu8qSWn7og==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthAs", - "data_type": "string", - "example_values": [ - "Anonymous" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthMechanism", - "data_type": "string", - "example_values": [ - "04" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthSource", - "data_type": "string", - "example_values": [ - "DM6NAM11FT055.eop-nam11.prod.protection.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationInterval", - "data_type": "string", - "example_values": [ - "1:00:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationIntervalReason", - "data_type": "string", - "example_values": [ - "OriginalSubmit" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTime", - "data_type": "string", - "example_values": [ - "18 Jun 2020 09:11:28.2531 (UTC)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTimeReason", - "data_type": "string", - "example_values": [ - "OriginalSubmit" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-MessageDirectionality", - "data_type": "string", - "example_values": [ - "Incoming" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-Network-Message-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-SCL", - "data_type": "string", - "example_values": [ - "-1" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Processed-By-BccFoldering", - "data_type": "string", - "example_values": [ - "15.20.3109.017" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStamped", - "data_type": "string", - "example_values": [ - "BN6PR18MB1492" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-EndToEndLatency", - "data_type": "string", - "example_values": [ - "00:00:02.7417647" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Has-Attach", - "data_type": "string", - "example_values": [ - "yes" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Oob-TLC-OOBClassifiers", - "data_type": "string", - "example_values": [ - "OLM:1728;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-PublicTrafficType", - "data_type": "string", - "example_values": [ - "Email" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-TNEF-Correlator", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-TrafficTypeDiagnostic", - "data_type": "string", - "example_values": [ - "BN6PR18MB1492:" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam", - "data_type": "string", - "example_values": [ - "BCL:0;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Mailbox-Delivery", - "data_type": "string", - "example_values": [ - "wl:1;pcwl:1;ucf:0;jmr:0;auth:0;dest:I;ENG:(750128)(520011016)(520004050)(702028)(944506458)(944626604);" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info", - "data_type": "string", - "example_values": [ - "La+CSxAnpzVJOXq7njrFPhIbsh0khleSwldy+W8NYDRsoyyPruPIiId4Avama7JyfzrxoExzhLk5pDn2lGPAJIpdcguiDSsDQg5T+iBCJgFeaEJXjhstECMi842/JGawB9WsiGw9Q/PpvjO5H/2fNLlQZVZW3AAQVZSsX3az4iOsv1Ggj4aYZRMKHmPAtniWOEtQD7zAEWC0jIZf613lWy3vxHfb/3+pV9X8zqPqazbyGy5Q14PICSNkKnvIw8rmeqJV8eSHhvR51Lchib6OIN4xOpLWxkSkBTt5B95RUPnpgPvgp2yLo0Q+EYRIabLDQ0kMsv+24+RnFmr9vo2gRNuFusw8iEPsVEQyhfgIWtBtsBpyvyykxcfa6lIdzQhixZH3Tlkdh1kb15wFS3Ooz3CjaWbY8jcUot5l1p08Ypsj6r7CpIo3xE6jE0x/EeUkDK3Fu/Ol0pOsJ1N5W4iJLdjqSQM3l/t9QWlcPhD8s6D7D7JM5OUHCeFEPr7sSL+P/5zTgBaeUvwtZrlQSH2GHc+5gPW8rkwlwJLJftVEid0gO2PUOrzItzME5PXYAcdx++sF3XC1YMPLet/jMpX8T7/z7+hxFxNyifgmGJ+DkNOec7yGkkcLBz6iCaHx7OrRGwDHIcdAtV85wCk3NEDDiKyHivQpwp/gY55W+wkLe7aqSHmFzm1rUSslx+DWz8w2EgSjJxOmf0JkoNKbTFl3FObkocR0lUUQUnETuoAXUqvpWGD5B69W9XXUM8c43ozz2oBZseheSAtkLil3tMIr/CMCMILPX/LdoErNtkmiFXCPqaLFSSeyO61oCMl6Ezndtwp22nwMPUg5ofG0kdqFuTW122umhy9C6h5BcREaLhWclSyqDoZPB9RvkRlI2kTRwuwbuFW3iOMzmVwxLIQH9K5JkxdMvC3hvNpjVgz7Q2ZnEF3xSNqeoWVQvkaIe8rQLUc8s+HMRUmSERGdfSuQJAx47g8PDs9s3rS/ThUSzIaljJPbUgXEnFg/G6h3I/yXLj2Nj2OG50snoI5jJmE4+69YmNwasdDZuYpnuQeFgu11HtsLniDthJdjEJyYC1utZNt9hgA+6JlLnm7Dxb43cSIiW8ev+3X+b2kREj2k/m8fSz7YgtoCB8AkuiVXRaH3EUiq8XCExbbWeynKRgwCZ6bzvfSiT3+cg+QQKPHFc/cgot56ta6X80tjhFodpTQNTE6V6C9QFHJ3JCVhsSzVifJAc8crI5hAcPbKFEIjinENcfpF/8reo2Yr1xFElhoX" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Originating-IP", - "data_type": "string", - "example_values": [ - "[2.39.180.162]" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Received", - "data_type": "string", - "example_values": [ - "by 2002:aa7:84d9:: with SMTP id x25mr2807688pfn.300.1592471487394; Thu, 18 Jun 2020 02:11:27 -0700 (PDT)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.subject", - "data_type": "string", - "example_values": [ - "test html" - ] - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "example_values": [ - "2020-06-18T09:11:31Z" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "example_values": [ - "test html" - ] - }, - { - "data_path": "action_result.data.*.uniqueBody.content", - "data_type": "string", - "example_values": [ - "
\\r\\n
\\r\\n

HTML heading

\\r\\nHTML body.
\\r\\n
\\r\\n" - ] - }, - { - "data_path": "action_result.data.*.uniqueBody.contentType", - "data_type": "string", - "example_values": [ - "html" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully fetched email" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "type": "json" - }, - "versions": "EQ(*)" - }, - { - "action": "run query", - "identifier": "run_query", - "description": "Search emails", - "verbose": "If the query or internet_message_id parameters are included, the subject, sender, body, and range parameters will be ignored. The internet_message_id parameter will take precedence over the query parameter.

For details on formatting the query parameter, refer to Microsoft Graph Query Parameters. Query parameters can include OData system query options or other supported parameters.

If the limit parameter is not included, the action will default to limiting to ten emails that match the rest of the query. The get_folder_id parameter should be enabled only when you specified folder name/folder path in the folder parameter. If you provide folder ID in the folder parameter and set get_folder_id parameter to true, it will throw an error of folder ID not found for given folder name (because the action considers folder parameter value as folder name/folder path). The folder parameter must be either a (case sensitive) well-known name [list here; https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0] or the internal o365 folder ID. The action supports searching for a folder that is nested within another. To copy in such a folder, specify the complete folder path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate.
When the search_well_known_folders parameter is set to true, action will ignore values provided in the folder and get_folder_id parameters and the user will get details from all 17 well-known folders which are listed below:
  • Archive
  • Clutter
  • Conflicts
  • Conversation History
  • Deleted Items
  • Drafts
  • Inbox
  • Junk Email
  • Local Failures
  • Msg Folder Root
  • Outbox
  • Recoverable Items Deletions
  • Scheduled
  • Search Folders
  • Sent Items
  • Server Failures
  • Sync Issues

If the limit parameter is provided, the user will get the number of messages provided in the limit from every folder if present.", - "type": "investigate", - "read_only": true, - "parameters": { - "email_address": { - "description": "User's email (mailbox to search in)", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 0 - }, - "folder": { - "description": "Destination folder; this must be either a (case-sensitive) well-known name or the internal o365 folder ID", - "data_type": "string", - "primary": true, - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path", - "msgoffice365 folder id" - ], - "default": "Inbox", - "order": 1 - }, - "search_well_known_folders": { - "description": "Checks all well known folders for messages, ignores folder name provided in parameter", - "data_type": "boolean", - "order": 2 - }, - "get_folder_id": { - "description": "Assume the folder parameter contains a folder name/folder path, separated by '/'(forward slash) ; i.e. Inbox/dir1/dir2/dir3. If this parameter is enabled, it retrieves the folder ID for the provided folder name/folder path automatically and replaces the parameter value", - "data_type": "boolean", - "default": true, - "order": 3 - }, - "subject": { - "description": "Substring to search in subject", - "data_type": "string", - "primary": true, - "contains": [ - "msgoffice365 subject" - ], - "order": 4 - }, - "body": { - "description": "Substring to search in body", - "data_type": "string", - "order": 5 - }, - "sender": { - "description": "Sender email address to match", - "data_type": "string", - "primary": true, - "contains": [ - "email" - ], - "order": 6 - }, - "limit": { - "description": "Maximum emails to return", - "data_type": "numeric", - "order": 7 - }, - "query": { - "description": "MS Graph query string", - "data_type": "string", - "order": 8 - }, - "internet_message_id": { - "description": "Internet message ID", - "data_type": "string", - "primary": true, - "contains": [ - "msgoffice365 internet message id" - ], - "order": 9 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.body", - "data_type": "string", - "example_values": [ - "How are you doing this fine evening?" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.folder", - "data_type": "string", - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path", - "msgoffice365 folder id" - ], - "example_values": [ - "Archive" - ] - }, - { - "data_path": "action_result.parameter.get_folder_id", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.internet_message_id", - "data_type": "string", - "contains": [ - "msgoffice365 internet message id" - ], - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.parameter.limit", - "data_type": "numeric", - "example_values": [ - 5 - ] - }, - { - "data_path": "action_result.parameter.query", - "data_type": "string", - "example_values": [ - "$filter=contains(subject,'Urgent')" - ] - }, - { - "data_path": "action_result.parameter.search_well_known_folders", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.sender", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.subject", - "data_type": "string", - "example_values": [ - "Just wanted to say hello" - ], - "contains": [ - "msgoffice365 subject" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFOpxtE\"" - ] - }, - { - "data_path": "action_result.data.*.@odata.type", - "data_type": "string", - "example_values": [ - "#test.abc.eventMessageRequests" - ] - }, - { - "data_path": "action_result.data.*.allowNewTimeProposals", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.bccRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "test3.test@test.com" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "test3.test@test.com" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.bccRecipients.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n
\\r\\nTest
\\r\\n
\\r\\n\\r\\n\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "text" - ] - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string", - "example_values": [ - "How are you doing this fine evening?" - ] - }, - { - "data_path": "action_result.data.*.categories", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.ccRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "test3.test@test.com" - ] - }, - { - "data_path": "action_result.data.*.ccRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "test3.test@test.com" - ] - }, - { - "data_path": "action_result.data.*.ccRecipients.email", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.ccRecipients.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string", - "example_values": [ - "CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFOpxtE" - ] - }, - { - "data_path": "action_result.data.*.conversationId", - "data_type": "string", - "example_values": [ - "AAQkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAQAGqbDRkVLxZMtetM-dKqAPo=" - ] - }, - { - "data_path": "action_result.data.*.conversationIndex", - "data_type": "string", - "example_values": [ - "AQHXHRZ01/QE6F/kQkdaSwXyspIYQagZQ==" - ] - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2017-10-30T22:32:42Z" - ] - }, - { - "data_path": "action_result.data.*.endDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2020-08-15T12:30:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.endDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.flag.flagStatus", - "data_type": "string", - "example_values": [ - "notFlagged" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "contains": [ - "msgoffice365 message id" - ], - "example_values": [ - "AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAAAAADJbdfk-sdvT4wwcqie92hZBwBBKXVvwEWISZupmqX4mJS3AACEV3zJAABBKXVvwEWISZupmqX4mJS3AAFOZwS4AAA=" - ] - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.inferenceClassification", - "data_type": "string", - "example_values": [ - "focused" - ] - }, - { - "data_path": "action_result.data.*.internetMessageId", - "data_type": "string", - "example_values": [ - "" - ], - "contains": [ - "msgoffice365 internet message id" - ] - }, - { - "data_path": "action_result.data.*.isAllDay", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDelegated", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDeliveryReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isOutOfDate", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isRead", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2017-10-30T22:32:53Z" - ] - }, - { - "data_path": "action_result.data.*.meetingMessageType", - "data_type": "string", - "example_values": [ - "meetingRequest" - ] - }, - { - "data_path": "action_result.data.*.meetingRequestType", - "data_type": "string", - "example_values": [ - "informationalUpdate" - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAAAAADJbdfk-sdvT4wwcqie92hZAQBBKXVvwEWISZupmqX4mJS3AACEV3zJAAA=" - ] - }, - { - "data_path": "action_result.data.*.previousEndDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.previousEndDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2020-08-15T12:30:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.previousEndDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.previousLocation", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.previousStartDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.previousStartDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2020-08-15T12:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.previousStartDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "example_values": [ - "2017-10-30T22:32:42Z" - ] - }, - { - "data_path": "action_result.data.*.recurrence", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.replyTo", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.replyTo.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "hellohi@test.com" - ] - }, - { - "data_path": "action_result.data.*.replyTo.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "hellohi@test.com" - ] - }, - { - "data_path": "action_result.data.*.responseRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.sentDateTime", - "data_type": "string", - "example_values": [ - "2017-10-30T22:32:37Z" - ] - }, - { - "data_path": "action_result.data.*.startDateTime.dateTime", - "data_type": "string", - "example_values": [ - "2020-08-15T12:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.startDateTime.timeZone", - "data_type": "string", - "example_values": [ - "UTC" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "example_values": [ - "Just wanted to say hello" - ], - "contains": [ - "msgoffice365 subject" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "Test@testdomain.abc.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Test Name" - ] - }, - { - "data_path": "action_result.data.*.type", - "data_type": "string", - "example_values": [ - "singleInstance" - ] - }, - { - "data_path": "action_result.data.*.vaultId", - "data_type": "string", - "example_values": [ - "719dbf72d7c0bc89d7e34306c08a0b66191902b9" - ], - "contains": [ - "sha1", - "vault id" - ] - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "example_values": [ - "https://outlook.office365.com/owa/?ItemID=AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAAAAADJbdfk%2FsdvT4wwcqie92hZBwBBKXVvwEWISZupmqX4mJS3AACEV3zJAABBKXVvwEWISZupmqX4mJS3AAFOZwS4AAA%3D&exvsurl=1&viewmodel=ReadMessageItem" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.summary.emails_matched", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Emails matched: 1" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Run query", - "type": "custom", - "height": 5, - "view": "office365_view.display_view" - }, - "versions": "EQ(*)" - }, - { - "action": "create folder", - "identifier": "create_folder", - "description": "Create a new folder", - "verbose": "Create a new folder either in the mailbox root or inside an existing folder. The action supports creating a folder that is nested within another. To create in such a folder, specify the complete path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate.", - "type": "generic", - "read_only": false, - "parameters": { - "email_address": { - "description": "User's email (mailbox to create folders)", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 0 - }, - "folder": { - "description": "Folder Name/Path. Use '/'to separate folder elements; i.e. Inbox/dir1/dir2/dir3", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path" - ], - "order": 1 - }, - "all_subdirs": { - "description": "Make any missing directories in the path if they don't exist instead of failing", - "data_type": "boolean", - "order": 2 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.all_subdirs", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.folder", - "data_type": "string", - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path" - ], - "example_values": [ - "Archive" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/$metadata#users('abc%def.test.com')/mailFolders/$entity" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAFOpxtE\"" - ] - }, - { - "data_path": "action_result.data.*.childFolderCount", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "column_name": "Display Name", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "column_name": "Folder ID", - "column_order": 1, - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAAAAADJbdfk-sdvT4wwcqie92hZBwBBKXVvwEWISZupmqX4mJS3AACEV3zJAABBKXVvwEWISZupmqX4mJS3AAFOZwS4AAA=" - ] - }, - { - "data_path": "action_result.data.*.isHidden", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "column_name": "Parent Folder ID", - "column_order": 2, - "contains": [ - "msgoffice365 folder id" - ], - "example_values": [ - "AAMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQAuAAAAAADJbdfk-sdvT4wwcqie92hZAQBBKXVvwEWISZupmqX4mJS3AACEV3zJAAA=" - ] - }, - { - "data_path": "action_result.data.*.sizeInBytes", - "data_type": "numeric", - "example_values": [ - 0 - ] - }, - { - "data_path": "action_result.data.*.totalItemCount", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "action_result.data.*.unreadItemCount", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "action_result.summary.folder", - "data_type": "string", - "example_values": [ - "AQMkAMExNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAFA6de0wAAAA==" - ] - }, - { - "data_path": "action_result.summary.folders created", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Emails matched: 1" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Create folder", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "get folder id", - "identifier": "get_folder_id", - "description": "Get the API ID of the folder", - "verbose": "The action supports searching a folder that is nested within another. To search in such a folder, specify the complete path using the '/' (forward slash) as the separator.
e.g. to search in a folder named phishing which is nested within (is a child of) Inbox, set the value as Inbox/phishing. If a folder name has a literal forward slash('/') in the name escape it with a backslash('\\') to differentiate.", - "type": "investigate", - "read_only": true, - "parameters": { - "email_address": { - "description": "User's email (mailbox)", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 0 - }, - "folder": { - "description": "Folder Name/Path. Use '/' to separate folder elements; i.e. Inbox/dir1/dir2/dir3", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path" - ], - "order": 1 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.folder", - "data_type": "string", - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path" - ], - "example_values": [ - "Test/Testy/subfolders" - ] - }, - { - "data_path": "action_result.data.*.folder", - "data_type": "string", - "column_name": "Folder Name", - "column_order": 1, - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path" - ], - "example_values": [ - "Test" - ] - }, - { - "data_path": "action_result.data.*.folder_id", - "data_type": "string", - "column_name": "Folder ID", - "column_order": 2, - "example_values": [ - "AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAEApxCRAAA=" - ], - "contains": [ - "msgoffice365 folder id" - ] - }, - { - "data_path": "action_result.data.*.path", - "data_type": "string", - "column_name": "Path", - "column_order": 0, - "contains": [ - "msgoffice365 mail folder", - "msgoffice365 mail folder path" - ] - }, - { - "data_path": "action_result.summary.folder_id", - "data_type": "string", - "example_values": [ - "AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAEApxCTAAA=" - ], - "contains": [ - "msgoffice365 folder id" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Folder id: AAMkAGFmNTRhODA4LWIxMjQtNDJjYy05NDM2LWQ5MzY1MGFhMTkzYwAuAAAAAADRlY7ewL4xToKRDciQog5UAQBvUzMoUJx2S4nbgxzZWx2PAAEApxCTAAA=" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "width": 12, - "title": "Get Folder ID", - "type": "table", - "height": 5 - }, - "versions": "EQ(*)" - }, - { - "action": "send email", - "identifier": "send_email", - "description": "Sends an email with optional text rendering. Attachments are allowed a Content-ID tag for reference within the html", - "verbose": "
Notes
  • If the from parameter is not provided, then the action will consider the username parameter provided in the asset configuration as the sender's email address.
  • The send email action is executed in two stages. Before sending an email it creates a draft of the email. Once the draft is successfully saved, the email is sent.
", - "type": "generic", - "read_only": false, - "parameters": { - "from": { - "description": "From field", - "data_type": "string", - "order": 0, - "primary": true, - "contains": [ - "email" - ] - }, - "to": { - "description": "List of recipients email addresses", - "data_type": "string", - "order": 1, - "required": true, - "primary": true, - "contains": [ - "email" - ], - "allow_list": true - }, - "cc": { - "description": "List of recipients email addresses to include on cc line", - "data_type": "string", - "order": 2, - "primary": true, - "contains": [ - "email" - ], - "allow_list": true - }, - "bcc": { - "description": "List of recipients email addresses to include on bcc line", - "data_type": "string", - "order": 3, - "contains": [ - "email" - ], - "allow_list": true, - "primary": true - }, - "subject": { - "description": "Message Subject", - "data_type": "string", - "order": 4, - "required": true - }, - "headers": { - "description": "Serialized json dictionary. Additional email headers to be added to the message", - "data_type": "string", - "order": 5 - }, - "body": { - "description": "Html rendering of message", - "data_type": "string", - "order": 6, - "required": true - }, - "attachments": { - "description": "List of vault ids of files to attach to the email. Vault id is used as content id", - "data_type": "string", - "order": 7, - "allow_list": true, - "contains": [ - "sha1", - "vault id" - ], - "primary": true - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.attachments", - "data_type": "string", - "contains": [ - "sha1", - "vault id" - ], - "example_values": [ - "da39a3ee5e6b4b0d3255bfef95601890afd80709" - ] - }, - { - "data_path": "action_result.parameter.bcc", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.body", - "data_type": "string", - "example_values": [ - "

Have a good time with these.

" - ] - }, - { - "data_path": "action_result.parameter.cc", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.from", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.parameter.headers", - "data_type": "string", - "example_values": [ - "{\"x-custom-header\":\"Custom value\"}" - ] - }, - { - "data_path": "action_result.parameter.subject", - "data_type": "string", - "example_values": [ - "Example subject" - ] - }, - { - "data_path": "action_result.parameter.to", - "data_type": "string", - "contains": [ - "email" - ], - "example_values": [ - "test@testdomain.abc.com" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/$metadata#users('user%40.abc.com')/messages(internetMessageHeaders,body,uniqueBody,sender,subject)/$entity" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAO8DBJl\"" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`Have a good time with these.\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "html" - ] - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string", - "example_values": [ - "Have a good time with these." - ], - "column_name": "Body Preview", - "column_order": 2 - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string", - "example_values": [ - "CQAAABYAAADTteE6Q2eCQKSqg19j6T+NAAYzSv5R" - ] - }, - { - "data_path": "action_result.data.*.conversationId", - "data_type": "string", - "example_values": [ - "AAQkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAQAORC3aOpHnZMsHD4-7L40sY=" - ] - }, - { - "data_path": "action_result.data.*.conversationIndex", - "data_type": "string", - "example_values": [ - "AQHZopYz5ELdo6kedkywcPj/svjSxg==" - ] - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2023-06-19T10:09:58Z" - ] - }, - { - "data_path": "action_result.data.*.flag.flagStatus", - "data_type": "string", - "example_values": [ - "notFlagged" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@test.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.name", - "data_type": "string", - "example_values": [ - "Ryan Edwards" - ] - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ], - "column_name": "Has Attachments", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA==" - ], - "contains": [ - "msgoffice365 message id" - ], - "column_name": "Message ID", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.inferenceClassification", - "data_type": "string", - "example_values": [ - "focused" - ] - }, - { - "data_path": "action_result.data.*.internetMessageId", - "data_type": "string", - "example_values": [ - "" - ], - "contains": [ - "msgoffice365 internet message id" - ] - }, - { - "data_path": "action_result.data.*.isDeliveryReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isRead", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2023-06-19T10:09:58Z" - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "example_values": [ - "AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAAAgEPAAAA" - ], - "contains": [ - "msgoffice365 folder id" - ] - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "example_values": [ - "2020-06-18T09:11:31Z" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sentDateTime", - "data_type": "string", - "example_values": [ - "2023-06-19T10:09:58Z" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "example_values": [ - "test html" - ], - "column_name": "Subject", - "column_order": 1 - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@test.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Ryan Edwards" - ] - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "example_values": [ - "https://outlook.office365.com/owa/?ItemID=AAkALgAAAAAAHYQDEapmEc2byACqAC%2FEWg0A07XhOkNngkCkqoNfY%2Bk%2FjQAGNNQOowAA&exvsurl=1&viewmodel=ReadMessageItem" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Successfully sent email" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "render": { - "type": "table" - }, - "versions": "EQ(*)" - }, - { - "action": "on poll", - "description": "Ingest emails from Office 365 using Graph API", - "type": "ingest", - "identifier": "on_poll", - "read_only": true, - "parameters": { - "start_time": { - "data_type": "numeric", - "description": "Parameter Ignored in this app", - "order": 0 - }, - "end_time": { - "data_type": "numeric", - "description": "Parameter Ignored in this app", - "order": 1 - }, - "container_id": { - "data_type": "string", - "description": "Parameter Ignored in this app", - "order": 2 - }, - "container_count": { - "data_type": "numeric", - "description": "Maximum number of emails to ingest", - "required": true, - "value": 100, - "order": 3 - }, - "artifact_count": { - "data_type": "numeric", - "description": "Parameter Ignored in this app", - "value": 1000, - "order": 4 - } - }, - "output": [], - "versions": "EQ(*)" - }, - { - "action": "update email", - "description": "Update an email on the server", - "verbose": "Currently, this action only updates the categories and subject of an email. To set multiple categories, please pass a comma-separated list to the category parameter.
NOTE: If the user tries to update the categories, then the existing categories of the email will be replaced with the new categories provided as input.", - "type": "generic", - "identifier": "update_email", - "read_only": false, - "parameters": { - "id": { - "description": "Message ID to delete", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "msgoffice365 message id" - ], - "order": 0 - }, - "email_address": { - "description": "Email address of the mailbox owner", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email" - ], - "order": 1 - }, - "subject": { - "description": "Subject to set", - "data_type": "string", - "order": 2 - }, - "categories": { - "description": "Categories to set", - "data_type": "string", - "order": 3, - "allow_list": true - } - }, - "render": { - "type": "table" - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.categories", - "data_type": "string", - "example_values": [ - "Yellow, Blue, Purple, red" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string", - "example_values": [ - "test@sample.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.parameter.id", - "data_type": "string", - "example_values": [ - "AAMkAGIyMTUxYTkzLWRjYjctNDFjMi04NTAxLTQzMDFkNDhlZmI5MQBGAAAAAACxQSnX8n2GS4cunBIQ2sV7BwCQhMsoV7EYSJF42ChR9SCxAAAAYCbsAACQhMsoV7EYSJF42ChR9SCxAAAAjh8bAAA=" - ], - "contains": [ - "msgoffice365 message id" - ] - }, - { - "data_path": "action_result.parameter.subject", - "data_type": "string", - "example_values": [ - "Both value are modified" - ] - }, - { - "data_path": "action_result.data.*.@odata.context", - "data_type": "string", - "example_values": [ - "https://test.abc.com/v1.0/$metadata#users('user%40.abc.com')/messages(internetMessageHeaders,body,uniqueBody,sender,subject)/$entity" - ], - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.@odata.etag", - "data_type": "string", - "example_values": [ - "W/\"CQAAABYAAABBKXVvwEWISZupmqX4mJS3AAO8DBJl\"" - ] - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string", - "example_values": [ - "`Have a good time with these.\\r\\n`" - ] - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string", - "example_values": [ - "html" - ] - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string", - "example_values": [ - "Have a good time with these." - ], - "column_name": "Body Preview", - "column_order": 2 - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string", - "example_values": [ - "CQAAABYAAADTteE6Q2eCQKSqg19j6T+NAAYzSv5R" - ] - }, - { - "data_path": "action_result.data.*.conversationId", - "data_type": "string", - "example_values": [ - "AAQkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAQAORC3aOpHnZMsHD4-7L40sY=" - ] - }, - { - "data_path": "action_result.data.*.conversationIndex", - "data_type": "string", - "example_values": [ - "AQHZopYz5ELdo6kedkywcPj/svjSxg==" - ] - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string", - "example_values": [ - "2023-06-19T10:09:58Z" - ] - }, - { - "data_path": "action_result.data.*.flag.flagStatus", - "data_type": "string", - "example_values": [ - "notFlagged" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@test.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.from.emailAddress.name", - "data_type": "string", - "example_values": [ - "Ryan Edwards" - ] - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "example_values": [ - true, - false - ], - "column_name": "Has Attachments", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "example_values": [ - "AQMkADU3NDk3MzJlLTY3MDQtNDE2Ny1iZDk1LTc4YjEwYzhmZDc5YQBGAAADyW3X5P7Hb0_MMHKonvdoWQcAQSl1b8BFiEmbqZql_JiUtwAAAgEMAAAAQSl1b8BFiEmbqZql_JiUtwADu9Tv8QAAAA==" - ], - "contains": [ - "msgoffice365 message id" - ], - "column_name": "Message ID", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string", - "example_values": [ - "normal" - ] - }, - { - "data_path": "action_result.data.*.inferenceClassification", - "data_type": "string", - "example_values": [ - "focused" - ] - }, - { - "data_path": "action_result.data.*.internetMessageId", - "data_type": "string", - "example_values": [ - "" - ], - "contains": [ - "msgoffice365 internet message id" - ] - }, - { - "data_path": "action_result.data.*.isDeliveryReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isRead", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.isReadReceiptRequested", - "data_type": "boolean", - "example_values": [ - true, - false - ] - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string", - "example_values": [ - "2023-06-19T10:09:58Z" - ] - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string", - "example_values": [ - "AQMkAGYxNGJmOWQyLTlhMjctNGRiOS1iODU0LTA1ZWE3ZmQ3NDU3MQAuAAADeDDJKaEf4EihMWU6SZgKbAEA07XhOkNngkCkqoNfY_k-jQAAAgEPAAAA" - ], - "contains": [ - "msgoffice365 folder id" - ] - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "example_values": [ - "2020-06-18T09:11:31Z" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.sentDateTime", - "data_type": "string", - "example_values": [ - "2023-06-19T10:09:58Z" - ] - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "example_values": [ - "test html" - ], - "column_name": "Subject", - "column_order": 1 - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.address", - "data_type": "string", - "example_values": [ - "test@test.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.name", - "data_type": "string", - "example_values": [ - "Ryan Edwards" - ] - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "example_values": [ - "https://outlook.office365.com/owa/?ItemID=AAkALgAAAAAAHYQDEapmEc2byACqAC%2FEWg0A07XhOkNngkCkqoNfY%2Bk%2FjQAGNNQOowAA&exvsurl=1&viewmodel=ReadMessageItem" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.message", - "data_type": "string", - "example_values": [ - "Create time: 2017-10-05T20:19:58Z\nSubject: Both value are modified\nSent time: 2017-10-03T21:31:20Z" - ] - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric", - "example_values": [ - 1 - ] - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric", - "example_values": [ - 1 - ] - } - ], - "versions": "EQ(*)" - }, - { - "action": "report message", - "identifier": "report_message", - "description": "Add the sender email into the report", - "verbose": "This action processes an email message and updates the sender classification based on the selected verdict (junk, notJunk, phish, unknown, or unknownFutureValue). When enabled, the message can optionally be moved to the corresponding folder based on the verdict. The action applies only if a message from the sender exists in the user's mailbox.", - "type": "contain", - "read_only": false, - "parameters": { - "message_id": { - "description": "Message ID to pick the sender of", - "data_type": "string", - "required": true, - "order": 0 - }, - "user_id": { - "description": "User ID to base the action of", - "data_type": "string", - "required": true, - "order": 1 - }, - "is_message_move_requested": { - "description": "Indicates whether the message should be moved out of current folder", - "data_type": "boolean", - "order": 2 - }, - "report_action": { - "description": "Indicates the type of action to be reported on the message", - "data_type": "string", - "required": true, - "order": 3, - "value_list": [ - "junk", - "notJunk", - "unknown", - "phish", - "unknownFutureValue" - ], - "name": "report_action" - } - }, - "output": [ - { - "data_path": "action_result.parameter.message_id", - "data_type": "string", - "column_name": "Message ID", - "column_order": 0 - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "string", - "column_name": "User ID", - "column_order": 1 - }, - { - "data_path": "action_result.parameter.is_message_move_requested", - "data_type": "boolean", - "column_name": "Message Move Requested", - "column_order": 2 - }, - { - "data_path": "action_result.parameter.report_action", - "data_type": "string", - "column_name": "Message Move Requested Type", - "column_order": 3 - }, - { - "data_path": "action_result.status", - "data_type": "string", - "column_name": "Status", - "column_order": 4, - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string" - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric" - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric" - } - ], - "render": { - "type": "table" - }, - "versions": "EQ(*)" - }, - { - "action": "block sender", - "identifier": "block_sender", - "description": "Add the sender email into the block list", - "verbose": "This action takes as input an email whose sender will be added to the Block Senders List. The message ID changes after the execution and is a required parameter for request hence undo action would require unique ID. Note that a message from the email address must exist in the user's mailbox before you can add the email address to or remove it from the Blocked Senders List.
  • If the move_to_junk_folder parameter is set to True, the sender of the target email message is added to the blocked sender list and the email message is moved to the Junk Email folder.
  • If the move_to_junk_folder attribute is set to False, the sender of the target email message is added to the blocked sender list and the email message is not moved from the folder.
To view the current Block Senders list, please read the following Powershell articles:
  • https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps
  • https://docs.microsoft.com/en-us/powershell/module/exchange/antispam-antimalware/Get-MailboxJunkEmailConfiguration?view=exchange-ps.
", - "type": "contain", - "read_only": false, - "parameters": { - "message_id": { - "description": "Message ID to pick the sender of", - "data_type": "string", - "required": true, - "order": 0 - }, - "user_id": { - "description": "User ID to base the action of", - "data_type": "string", - "required": true, - "order": 1 - }, - "move_to_junk_folder": { - "description": "Should the email be moved to the junk folder", - "data_type": "boolean", - "order": 2 - } - }, - "output": [ - { - "data_path": "action_result.parameter.message_id", - "data_type": "string", - "column_name": "Message ID", - "column_order": 0 - }, - { - "data_path": "action_result.parameter.move_to_junk_folder", - "data_type": "boolean", - "column_name": "Move To Junk Folder", - "column_order": 1 - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "boolean", - "column_name": "User ID", - "column_order": 2 - }, - { - "data_path": "action_result.status", - "data_type": "string", - "column_name": "Status", - "column_order": 3, - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string" - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric" - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric" - } - ], - "render": { - "type": "table" - }, - "versions": "EQ(*)" - }, - { - "action": "unblock sender", - "identifier": "unblock_sender", - "description": "Remove the sender email from the block list", - "verbose": "This action takes as input an email whose sender will be removed from the Block Senders List. The message ID changes after the execution and is a required parameter for request hence undo action would require unique ID. Note that a message from the email address must exist in the user's mailbox before you can add the email address to or remove it from the Blocked Senders List.
  • If the move_to_inbox parameter is set to True, the sender of the target email message is removed from the blocked sender list and the email message is moved from the Junk Email folder.
  • If the move_to_inbox attribute is set to False, the sender of the target email message is removed from the blocked sender list and the email message is not moved from the folder.
To view the current Block Senders list, please read the following Powershell articles:
  • https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/connect-to-exchange-online-powershell/connect-to-exchange-online-powershell?view=exchange-ps
  • https://docs.microsoft.com/en-us/powershell/module/exchange/antispam-antimalware/Get-MailboxJunkEmailConfiguration?view=exchange-ps.
", - "type": "contain", - "read_only": false, - "parameters": { - "message_id": { - "description": "Message ID to pick the sender of", - "data_type": "string", - "required": true, - "order": 0 - }, - "user_id": { - "description": "User ID to base the action of", - "data_type": "string", - "required": true, - "order": 1 - }, - "move_to_inbox": { - "description": "Should the email be moved to the inbox folder", - "data_type": "boolean", - "order": 2 - } - }, - "output": [ - { - "data_path": "action_result.parameter.message_id", - "data_type": "string", - "column_name": "Message ID", - "column_order": 0 - }, - { - "data_path": "action_result.parameter.move_to_inbox", - "data_type": "boolean", - "column_name": "Move To Inbox Folder", - "column_order": 1 - }, - { - "data_path": "action_result.parameter.user_id", - "data_type": "boolean", - "column_name": "User ID", - "column_order": 2 - }, - { - "data_path": "action_result.status", - "data_type": "string", - "column_name": "Status", - "column_order": 3, - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string" - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric" - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric" - } - ], - "render": { - "type": "table" - }, - "versions": "EQ(*)" - }, - { - "action": "resolve name", - "identifier": "resolve_name", - "description": "Verify aliases and resolve display names to the appropriate user", - "verbose": "Resolve an Alias name or email address, gathering complex data about the user.", - "type": "investigate", - "read_only": true, - "parameters": { - "email": { - "description": "Name to resolve", - "data_type": "string", - "required": true, - "primary": true, - "contains": [ - "email", - "string" - ], - "order": 0 - } - }, - "output": [ - { - "data_path": "action_result.parameter.email", - "data_type": "string", - "contains": [ - "email", - "string" - ], - "column_name": "Email", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "contains": [ - "msgoffice365 id" - ], - "column_name": "ID", - "column_order": 1 - }, - { - "data_path": "action_result.data.*.userPrincipalName", - "data_type": "string", - "contains": [ - "msgoffice365 user principal name" - ], - "column_name": "Principal Name", - "column_order": 2 - }, - { - "data_path": "action_result.data.*.givenName", - "data_type": "string", - "contains": [ - "msgoffice365 given name" - ], - "column_name": "Given Name", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.surname", - "data_type": "string", - "contains": [ - "msgoffice365 surname" - ], - "column_name": "Surname", - "column_order": 4 - }, - { - "data_path": "action_result.data.*.displayName", - "data_type": "string", - "contains": [ - "msgoffice365 display name" - ], - "column_name": "Display Name", - "column_order": 5 - }, - { - "data_path": "action_result.data.*.mailNickname", - "data_type": "string", - "contains": [ - "msgoffice365 mail nickname" - ], - "column_name": "Mail Nickname", - "column_order": 6 - }, - { - "data_path": "action_result.data.*.mail", - "data_type": "string", - "contains": [ - "email" - ], - "column_name": "Mail", - "column_order": 7 - }, - { - "data_path": "action_result.data.*.otherMails", - "data_type": "string", - "contains": [ - "email list" - ], - "column_name": "Other Mails", - "column_order": 8 - }, - { - "data_path": "action_result.data.*.proxyAddresses", - "data_type": "string", - "contains": [ - "email list" - ], - "column_name": "Proxy Email Addresses", - "column_order": 9 - }, - { - "data_path": "action_result.data.*.jobTitle", - "data_type": "string", - "contains": [ - "msgoffice365 job title" - ], - "column_name": "Job Title", - "column_order": 10 - }, - { - "data_path": "action_result.data.*.officeLocation", - "data_type": "string", - "contains": [ - "msgoffice365 office location" - ], - "column_name": "Office Location", - "column_order": 11 - }, - { - "data_path": "action_result.data.*.value", - "data_type": "string", - "contains": [ - "msgoffice365 user purpose" - ], - "column_name": "User Purpose", - "column_order": 12 - }, - { - "data_path": "action_result.data.*.mobilePhone", - "data_type": "string", - "contains": [ - "msgoffice365 mobile phone" - ], - "column_name": "Mobile Phone", - "column_order": 13 - }, - { - "data_path": "action_result.data.*.businessPhones", - "data_type": "string", - "contains": [ - "msgoffice365 buisness phones" - ], - "column_name": "Business Phones", - "column_order": 14 - }, - { - "data_path": "action_result.data.*.preferredLanguage", - "data_type": "string", - "contains": [ - "msgoffice365 preferred language" - ], - "column_name": "Preferred Language", - "column_order": 15 - }, - { - "data_path": "action_result.data.*.state", - "data_type": "string", - "contains": [ - "msgoffice365 state" - ], - "column_name": "State", - "column_order": 16 - }, - { - "data_path": "action_result.data.*.postalCode", - "data_type": "string", - "contains": [ - "msgoffice365 postal code" - ], - "column_name": "Postal Code", - "column_order": 17 - }, - { - "data_path": "action_result.summary", - "data_type": "string" - }, - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.message", - "data_type": "string" - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric" - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric" - } - ], - "render": { - "width": 12, - "title": "resolve name", - "type": "custom", - "height": 5, - "view": "office365_view.display_view" - }, - "versions": "EQ(*)" - }, - { - "action": "get mailbox messages", - "identifier": "get_mailbox_messages", - "description": "Retrieves messages from a specified mailbox folder with advanced functionality", - "type": "investigate", - "read_only": true, - "parameters": { - "email_address": { - "description": "Email address of the mailbox", - "data_type": "string", - "required": true, - "primary": true, - "order": 0 - }, - "folder": { - "description": "Folder to retrieve messages", - "data_type": "string", - "default": "inbox", - "order": 1 - }, - "limit": { - "description": "Maximum number of messages to retrieve (should not exceed 100 per request)", - "data_type": "numeric", - "default": 100, - "order": 2 - }, - "offset": { - "description": "Number of messages to skip before retrieving results", - "data_type": "numeric", - "default": 0, - "order": 3 - }, - "start_date": { - "description": "Start date for filtering messages (format: YYYY-MM-DD)", - "data_type": "string", - "order": 4 - }, - "end_date": { - "description": "End date for filtering messages (format: YYYY-MM-DD)", - "data_type": "string", - "order": 5 - }, - "download_attachments": { - "description": "Download email attachments to vault", - "data_type": "boolean", - "default": false, - "order": 6 - }, - "download_email": { - "description": "Download email as EML file to vault", - "data_type": "boolean", - "default": false, - "order": 7 - }, - "extract_headers": { - "description": "Include email headers in results", - "data_type": "boolean", - "default": false, - "order": 8 - }, - "plus_ingest": { - "description": "If enabled, messages will be also ingested like on_poll", - "data_type": "boolean", - "order": 9 - } - }, - "output": [ - { - "data_path": "action_result.status", - "data_type": "string", - "example_values": [ - "success", - "failed" - ] - }, - { - "data_path": "action_result.parameter.email_address", - "data_type": "string" - }, - { - "data_path": "action_result.parameter.folder", - "data_type": "string" - }, - { - "data_path": "action_result.parameter.limit", - "data_type": "numeric" - }, - { - "data_path": "action_result.parameter.offset", - "data_type": "numeric" - }, - { - "data_path": "action_result.parameter.start_date", - "data_type": "string" - }, - { - "data_path": "action_result.parameter.end_date", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.id", - "data_type": "string", - "column_name": "MESSAGE ID", - "column_order": 0 - }, - { - "data_path": "action_result.data.*.body.content", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.body.contentType", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.flag.flagStatus", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.from.emailAddress.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.from.emailAddress.address", - "data_type": "string", - "column_name": "From", - "column_order": 1 - }, - { - "data_path": "action_result.data.*.isRead", - "data_type": "boolean", - "column_name": "Is Read", - "column_order": 2 - }, - { - "data_path": "action_result.data.*.sender.emailAddress.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.sender.emailAddress.address", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.isDraft", - "data_type": "boolean" - }, - { - "data_path": "action_result.data.*.replyTo.*.emailAddress.address", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.replyTo.*.emailAddress.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.subject", - "data_type": "string", - "column_name": "Subject", - "column_order": 3 - }, - { - "data_path": "action_result.data.*.webLink", - "data_type": "string", - "contains": [ - "url" - ] - }, - { - "data_path": "action_result.data.*.changeKey", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.categories.*.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.importance", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.uniqueBody.content", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.uniqueBody.contentType", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.bodyPreview", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.ccRecipients.*.emailAddress.address", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.ccRecipients.*.emailAddress.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.sentDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.toRecipients.*.emailAddress.address", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.bccRecipients.*.emailAddress.address", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.bccRecipients.*.emailAddress.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.conversationId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.hasAttachments", - "data_type": "boolean", - "column_name": "Has Attachments", - "column_order": 4 - }, - { - "data_path": "action_result.data.*.parentFolderId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.createdDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.receivedDateTime", - "data_type": "string", - "column_name": "Received Time", - "column_order": 5 - }, - { - "data_path": "action_result.data.*.conversationIndex", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.internetMessageId", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.lastModifiedDateTime", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.*.name", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.*.value", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Accept-Language", - "data_type": "string", - "example_values": [ - "en-US" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Authentication-Results", - "data_type": "string", - "example_values": [ - "spf=pass (sender IP is 209.85.210.171) smtp.mailfrom=testdomain.com; .abc.com; dkim=pass (signature was verified) header.d=testdomain.com.20150623.gappssmtp.com;.abc.com; dmarc=pass action=none header.from=testdomain.com;compauth=pass reason=100" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Language", - "data_type": "string", - "example_values": [ - "en-US" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Transfer-Encoding", - "data_type": "string", - "example_values": [ - "binary" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Content-Type", - "data_type": "string", - "example_values": [ - "multipart/related" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.DKIM-Signature", - "data_type": "string", - "example_values": [ - "v=1; a=rsa-sha256; c=relaxed/relaxed; d=testdomain.com.20150623.gappssmtp.com; s=20150623; h=message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=avAAeJ8jF08K4oIBhxTirRmyB+SXHwdU0zdxv7eqs/zWaWWcgmT0007KP560TTgo5u oD4nb6TvKxpRyWW4QwmkbuMIwHsMvehd2l1gispV3AawyGJjpmN7ErVYfLtIkz2Tap3V YxmluV+SqeyyxTU8pFAEZ7+2C2lOb1DO5TC7xCMv+dyzevSscJdbeN0dFkG+C93zCqkg w2fxubx2HDD7b/U6m2wXllYhH608wKJ/qYzyvQyqxYqNiQOtPRg2gw4sZ2UgN3+UQyVq 8ubO39ZuqakJpzEzYMw10d6E7SQhvHDJH7mFwhBlzhvOpb2gLJDN8n8dJaZo05BozQqq MsvA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Date", - "data_type": "string", - "example_values": [ - "Thu, 18 Jun 2020 02:11:26 -0700" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.From", - "data_type": "string", - "example_values": [ - "\"Test\" " - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.In-Reply-To", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.MIME-Version", - "data_type": "string", - "example_values": [ - "1.0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Message-ID", - "data_type": "string", - "example_values": [ - "<5eeb2fbe.1c69fb81.22b4b.676a@mx.test.com>" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Received", - "data_type": "string", - "example_values": [ - "from localhost.localdomain (host-240.test.com. [204.107.141.240]) by tset.abc.com with UTF8SMTPSA id ng12sm1923252pjb.15.2020.06.18.02.11.26 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 18 Jun 2020 02:11:26 -0700 (PDT)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Received-SPF", - "data_type": "string", - "example_values": [ - "Pass (protection.test.com: domain of testdomain.com designates 209.85.210.171 as permitted sender) receiver=protection.test.com; client-ip=209.85.210.171; helo=mail-pf1-f171.test.com;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.References", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Return-Path", - "data_type": "string", - "example_values": [ - "notifications@testdomain.com" - ], - "contains": [ - "email" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Subject", - "data_type": "string", - "example_values": [ - "Fw: Email having different attachments" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Thread-Index", - "data_type": "string", - "example_values": [ - "AQHWZLqyXR4k4Sc6skyFCMPITcMsbKpGS7Bm" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.Thread-Topic", - "data_type": "string", - "example_values": [ - "Email having different attachments" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.To", - "data_type": "string", - "example_values": [ - "\"Test\" " - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-EOPAttributedMessage", - "data_type": "string", - "example_values": [ - "0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-EOPTenantAttributedMessage", - "data_type": "string", - "example_values": [ - "a417c578-c7ee-480d-a225-d48057e74df5:0" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Forefront-Antispam-Report", - "data_type": "string", - "example_values": [ - "CIP:209.85.210.171;CTRY:US;LANG:en;SCL:-1;SRV:;IPV:NLI;SFV:SFE;H:mail-pf1-f171.test.com;PTR:mail-pf1-f171.test.com;CAT:NONE;SFTY:;SFS:;DIR:INB;SFP:;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Gm-Message-State", - "data_type": "string", - "example_values": [ - "AOAM533ynFERIhSIewEEkj4b8B1rPNOEeie1IxBdrd55treEMtBa1jkL\tcO5ee4Ff6p0FYedfFtVtHKiCglGTpFTOSw==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Google-DKIM-Signature", - "data_type": "string", - "example_values": [ - "v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:message-id:date:mime-version:from:to:subject; bh=tlTaRbacq4aWozhUPvcWg8i8flbpYQGZNs27nncn83I=; b=fPT47NIiheeY6GM0bxUOlsmnOgN4WuiOlalFvZqrAiFiOoYk6zrznvgIcAtiHZ4nxE naQAa+mZs5svqRjib3YI52OvR5U8MitIYaa0Rt3LyYSUO1s3iKTUs4nHyRnqPt1skNl7 2OUwsZPXo3ShJDw/uxZRu/cuN1iIfeuE02PrbR04p4D8+1XRslqt/Xqm/bOWKUauqZWe dH1E7meFY01hXxODreO4nWHIhsZgr49TpP/OqRyFcyKHHFFg2sPGXz+QNah6jP4YQUYd Tty2wzOX3nc/YS7TkVo3ORmbzh9o+UZaqH8wHbQlyTdklYxoMPvJwZTo72rTxZeqiJ9E J7PQ==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Google-Smtp-Source", - "data_type": "string", - "example_values": [ - "ABdhPJxrYC7raBubCCIOmauxmxryzS9KsihTN6XCRgaNp2rDrG71TVxryzYCtelFOZ2Xj1LzcYIiMA==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-AntiSpam-MessageData", - "data_type": "string", - "example_values": [ - "VSM9HTzub/OH3NCwKXEQqkkzjnhdw5kXsgd9WM0SRgZ0qRdPg5D9/o3LA7lf8ziXc5k0mm9M5mHvFoYePXNXs/MGhGdBGxa/qUQ+FVHA2mDgfPkamJCEZxz//OX/uruTDo+zF4p9D1dQJpnIpx1M75OhuvrHX/BxWWzyAh78DXfF214YHdyFBCYepwl56CS7+fSGQL/r3p+OvWIBnIkISC+HJljSro2k47pPPAkspMhoUkb+zklyENFjez+JcEHYlih2FiNeUO8kb9b7qvlm3zPK98HLspzDh4BojpQ6Ff330iy7nfIK726tCMByxjOdnEQSB9Ua2sbE5gxSeeWL8MB5DHcQSSsXg+sR8w4gXrXLO3meE0lNQKRoAv2b1U0Q+yM0QBqeQWlymZG21bKeuH4gtAFQvfXNjoCtIbBQK1n7ZnL7fI21FJZRcMcKEneus6gLYUqD4PdLEq9FEGbfgiLmVYeUAL2A0Q/gectvL1OVudtHVR5gFMJKt65F1OtS04CPulfLLFSl1F4AzpjjtBSyQcK9R7bOsjoHxQXPMd9fMCzMSIq5f551pO0klKqWY7l11Un2Noj6CA7EtXiD1bTv8JmYQEKR+0HTZagNd+79GeTvKjxTvt9MkyO8k3aqWyNqT331ITnVICtksN1TVMCp8GVeDudNMr2PLSW0alOduR5unuEgTWrqHoaTGOovQx0PVjudNlpZ80ANK9hqaC/ZhLLOtNpJ3fZnjs06PzrPLGhE/IeccY1n8sYDvGm1QA9TN6JaaGPl1Pj6ecy16k0XuF/PKGHTL0M4LCpxSS6T87oFFH1zHkKtmbJp3aAI4bt3ihbQmwFb29JyMgL7ZOy+zrIwXGILh1KQGWQQv1uXXnAuqQy29HeFXs6D2hDHxHlBk5ZQ+vgRtsvRvGnq58vJ3CapjntfL3pOINUj1avLyAZxjasBWMTwaZs9JQ4ZIMekzkIk05lh9XfDSeULk2yKaH8YSCC6ENUHxSWa6pPHJfOdp9kXwOtlp09/VTTAikKy862k9ybN4bRWZB45B9Pv5scna8IX3rthIXUih8c=" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthAs", - "data_type": "string", - "example_values": [ - "Internal" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-AuthSource", - "data_type": "string", - "example_values": [ - "SJ0QA11MB4941.namprd11.prod.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-FromEntityHeader", - "data_type": "string", - "example_values": [ - "Internet" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Id", - "data_type": "string", - "example_values": [ - "a417c578-c7ee-480d-a225-d48057e74df5" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-MailboxType", - "data_type": "string", - "example_values": [ - "HOSTED" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-Network-Message-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-OriginalArrivalTime", - "data_type": "string", - "example_values": [ - "18 Jun 2020 09:11:28.2511 (UTC)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-CrossTenant-UserPrincipalName", - "data_type": "string", - "example_values": [ - "bs91VnpEPjrqCnvlIeymwO6ye4Q8rggHggVNUPUbV/tC9uuFPVFOYg7e/Cd0MeGmSqT4AlLW0Nn4ZeEqNieSf/D1gp5iLz/YkwjXhYUSJnLRb/csQN4sRMMZsX3LUkKkwVpifaeJzoukLu8qSWn7og==" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthAs", - "data_type": "string", - "example_values": [ - "Anonymous" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthMechanism", - "data_type": "string", - "example_values": [ - "04" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-AuthSource", - "data_type": "string", - "example_values": [ - "DM6NAM11FT055.eop-nam11.prod.protection.test.com" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationInterval", - "data_type": "string", - "example_values": [ - "1:00:00:00.0000000" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationIntervalReason", - "data_type": "string", - "example_values": [ - "OriginalSubmit" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTime", - "data_type": "string", - "example_values": [ - "18 Jun 2020 09:11:28.2531 (UTC)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-ExpirationStartTimeReason", - "data_type": "string", - "example_values": [ - "OriginalSubmit" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-MessageDirectionality", - "data_type": "string", - "example_values": [ - "Incoming" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-Network-Message-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Organization-SCL", - "data_type": "string", - "example_values": [ - "-1" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Processed-By-BccFoldering", - "data_type": "string", - "example_values": [ - "15.20.3109.017" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-CrossTenantHeadersStamped", - "data_type": "string", - "example_values": [ - "BN6PR18MB1492" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Exchange-Transport-EndToEndLatency", - "data_type": "string", - "example_values": [ - "00:00:02.7417647" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Has-Attach", - "data_type": "string", - "example_values": [ - "yes" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Office365-Filtering-Correlation-Id", - "data_type": "string", - "example_values": [ - "4b1ef179-4fe7-4248-7ec0-08d81367956e" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-Oob-TLC-OOBClassifiers", - "data_type": "string", - "example_values": [ - "OLM:1728;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-PublicTrafficType", - "data_type": "string", - "example_values": [ - "Email" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-TNEF-Correlator", - "data_type": "string", - "example_values": [ - "" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-MS-TrafficTypeDiagnostic", - "data_type": "string", - "example_values": [ - "BN6PR18MB1492:" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam", - "data_type": "string", - "example_values": [ - "BCL:0;" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Mailbox-Delivery", - "data_type": "string", - "example_values": [ - "wl:1;pcwl:1;ucf:0;jmr:0;auth:0;dest:I;ENG:(750128)(520011016)(520004050)(702028)(944506458)(944626604);" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Microsoft-Antispam-Message-Info", - "data_type": "string", - "example_values": [ - "La+CSxAnpzVJOXq7njrFPhIbsh0khleSwldy+W8NYDRsoyyPruPIiId4Avama7JyfzrxoExzhLk5pDn2lGPAJIpdcguiDSsDQg5T+iBCJgFeaEJXjhstECMi842/JGawB9WsiGw9Q/PpvjO5H/2fNLlQZVZW3AAQVZSsX3az4iOsv1Ggj4aYZRMKHmPAtniWOEtQD7zAEWC0jIZf613lWy3vxHfb/3+pV9X8zqPqazbyGy5Q14PICSNkKnvIw8rmeqJV8eSHhvR51Lchib6OIN4xOpLWxkSkBTt5B95RUPnpgPvgp2yLo0Q+EYRIabLDQ0kMsv+24+RnFmr9vo2gRNuFusw8iEPsVEQyhfgIWtBtsBpyvyykxcfa6lIdzQhixZH3Tlkdh1kb15wFS3Ooz3CjaWbY8jcUot5l1p08Ypsj6r7CpIo3xE6jE0x/EeUkDK3Fu/Ol0pOsJ1N5W4iJLdjqSQM3l/t9QWlcPhD8s6D7D7JM5OUHCeFEPr7sSL+P/5zTgBaeUvwtZrlQSH2GHc+5gPW8rkwlwJLJftVEid0gO2PUOrzItzME5PXYAcdx++sF3XC1YMPLet/jMpX8T7/z7+hxFxNyifgmGJ+DkNOec7yGkkcLBz6iCaHx7OrRGwDHIcdAtV85wCk3NEDDiKyHivQpwp/gY55W+wkLe7aqSHmFzm1rUSslx+DWz8w2EgSjJxOmf0JkoNKbTFl3FObkocR0lUUQUnETuoAXUqvpWGD5B69W9XXUM8c43ozz2oBZseheSAtkLil3tMIr/CMCMILPX/LdoErNtkmiFXCPqaLFSSeyO61oCMl6Ezndtwp22nwMPUg5ofG0kdqFuTW122umhy9C6h5BcREaLhWclSyqDoZPB9RvkRlI2kTRwuwbuFW3iOMzmVwxLIQH9K5JkxdMvC3hvNpjVgz7Q2ZnEF3xSNqeoWVQvkaIe8rQLUc8s+HMRUmSERGdfSuQJAx47g8PDs9s3rS/ThUSzIaljJPbUgXEnFg/G6h3I/yXLj2Nj2OG50snoI5jJmE4+69YmNwasdDZuYpnuQeFgu11HtsLniDthJdjEJyYC1utZNt9hgA+6JlLnm7Dxb43cSIiW8ev+3X+b2kREj2k/m8fSz7YgtoCB8AkuiVXRaH3EUiq8XCExbbWeynKRgwCZ6bzvfSiT3+cg+QQKPHFc/cgot56ta6X80tjhFodpTQNTE6V6C9QFHJ3JCVhsSzVifJAc8crI5hAcPbKFEIjinENcfpF/8reo2Yr1xFElhoX" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Originating-IP", - "data_type": "string", - "example_values": [ - "[2.39.180.162]" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.X-Received", - "data_type": "string", - "example_values": [ - "by 2002:aa7:84d9:: with SMTP id x25mr2807688pfn.300.1592471487394; Thu, 18 Jun 2020 02:11:27 -0700 (PDT)" - ] - }, - { - "data_path": "action_result.data.*.internetMessageHeaders.subject", - "data_type": "string", - "example_values": [ - "test html" - ] - }, - { - "data_path": "action_result.data.*.isReadReceiptRequested", - "data_type": "boolean" - }, - { - "data_path": "action_result.data.*.inferenceClassification", - "data_type": "string" - }, - { - "data_path": "action_result.data.*.isDeliveryReceiptRequested", - "data_type": "boolean" - }, - { - "data_path": "action_result.summary.total_messages", - "data_type": "numeric" - }, - { - "data_path": "action_result.summary.duplicate_emails", - "data_type": "numeric" - }, - { - "data_path": "action_result.summary.failed_emails", - "data_type": "numeric" - }, - { - "data_path": "action_result.summary.new_emails_ingested", - "data_type": "numeric" - }, - { - "data_path": "action_result.message", - "data_type": "string" - }, - { - "data_path": "summary.total_objects", - "data_type": "numeric" - }, - { - "data_path": "summary.total_objects_successful", - "data_type": "numeric" - }, - { - "data_path": "action_result.parameter.download_attachments", - "data_type": "boolean" - }, - { - "data_path": "action_result.parameter.download_email", - "data_type": "boolean" - }, - { - "data_path": "action_result.parameter.extract_headers", - "data_type": "boolean" - }, - { - "data_path": "action_result.parameter.plus_ingest", - "data_type": "boolean" - } - ], - "render": { - "type": "table" - }, - "versions": "EQ(*)" - } - ], - "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" - }, - { - "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" - }, - { - "module": "typing_extensions", - "input_file": "wheels/py3/typing_extensions-4.15.0-py3-none-any.whl" - } - ] - }, - "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" - }, - { - "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" - } - ] - } -} diff --git a/office365_connector.py b/office365_connector.py deleted file mode 100644 index 3db9d56..0000000 --- a/office365_connector.py +++ /dev/null @@ -1,3605 +0,0 @@ -# File: office365_connector.py -# -# Copyright (c) 2017-2025 Splunk Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -# either express or implied. See the License for the specific language governing permissions -# and limitations under the License. -# -import base64 -import grp -import json -import os -import pathlib -import pwd -import re -import sys -import tempfile -import time -from copy import deepcopy -from datetime import datetime - -import encryption_helper -import msal -import phantom.app as phantom -import phantom.rules as ph_rules -import phantom.utils as util -import phantom.vault as phantom_vault -import requests -from bs4 import BeautifulSoup, UnicodeDammit -from django.http import HttpResponse -from phantom.action_result import ActionResult -from phantom.base_connector import BaseConnector -from phantom.vault import Vault - -from office365_consts import * -from process_email import ProcessEmail - - -TC_FILE = "oauth_task.out" -SERVER_TOKEN_URL = "https://login.microsoftonline.com/{0}/oauth2/v2.0/token" -MSGOFFICE365_AUTHORITY_URL = "https://login.microsoftonline.com/{tenant}" -MSGRAPH_API_URL = "https://graph.microsoft.com" -MAX_END_OFFSET_VAL = 2147483646 -MSGOFFICE365_DEFAULT_SCOPE = "https://graph.microsoft.com/.default" - - -class ReturnException(Exception): - pass - - -class RetVal(tuple): - def __new__(cls, val1, val2): - return tuple.__new__(RetVal, (val1, val2)) - - -def _load_app_state(asset_id, app_connector=None): - """This function is used to load the current state file. - - :param asset_id: asset_id - :param app_connector: Object of app_connector class - :return: state: Current state file as a dictionary - """ - - asset_id = str(asset_id) - if not asset_id or not asset_id.isalnum(): - if app_connector: - app_connector.debug_print("In _load_app_state: Invalid asset_id") - return {} - - app_dir = os.path.dirname(os.path.abspath(__file__)) - state_file = f"{app_dir}/{asset_id}_state.json" - real_state_file_path = os.path.abspath(state_file) - if not os.path.dirname(real_state_file_path) == app_dir: - if app_connector: - app_connector.debug_print("In _load_app_state: Invalid asset_id") - return {} - - state = {} - try: - with open(real_state_file_path) as state_file_obj: - state_file_data = state_file_obj.read() - state = json.loads(state_file_data) - except Exception as e: - if app_connector: - error_msg = _get_error_msg_from_exception(e, app_connector) - app_connector.debug_print(f"In _load_app_state: {error_msg}") - - if app_connector: - app_connector.debug_print("Loaded state: ", state) - - try: - if "code" in state: - state["code"] = encryption_helper.decrypt(state["code"], asset_id) - except Exception as e: - if app_connector: - error_msg = _get_error_msg_from_exception(e, app_connector) - app_connector.debug_print(f"{MSGOFFICE365_DECRYPTION_ERROR}: {error_msg}") - state = {} - - return state - - -def _save_app_state(state, asset_id, app_connector=None): - """This function is used to save current state in file. - - :param state: Dictionary which contains data to write in state file - :param asset_id: asset_id - :param app_connector: Object of app_connector class - :return: status: phantom.APP_SUCCESS - """ - - asset_id = str(asset_id) - if not asset_id or not asset_id.isalnum(): - if app_connector: - app_connector.debug_print("In _save_app_state: Invalid asset_id") - return {} - - app_dir = os.path.split(__file__)[0] - state_file = f"{app_dir}/{asset_id}_state.json" - - real_state_file_path = os.path.abspath(state_file) - if not os.path.dirname(real_state_file_path) == app_dir: - if app_connector: - app_connector.debug_print("In _save_app_state: Invalid asset_id") - return {} - - try: - if "code" in state: - state["code"] = encryption_helper.encrypt(state["code"], asset_id) - except Exception as e: - if app_connector: - error_msg = _get_error_msg_from_exception(e, app_connector) - app_connector.debug_print(f"{MSGOFFICE365_ENCRYPTION_ERROR}: {error_msg}") - - if app_connector: - app_connector.debug_print("Saving state: ", state) - - try: - with open(real_state_file_path, "w+") as state_file_obj: - state_file_obj.write(json.dumps(state)) - except Exception as e: - error_msg = _get_error_msg_from_exception(e, app_connector) - if app_connector: - app_connector.debug_print(f"Unable to save state file: {error_msg}") - print(f"Unable to save state file: {error_msg}") - return phantom.APP_ERROR - - return phantom.APP_SUCCESS - - -def _get_error_msg_from_exception(e, app_connector=None): - """ - Get appropriate error message from the exception. - :param e: Exception object - :return: error message - """ - error_code = None - error_msg = MSGOFFICE365_ERROR_MSG_UNAVAILABLE - if app_connector: - app_connector.error_print("Error occurred.", dump_object=e) - - try: - if hasattr(e, "args"): - if len(e.args) > 1: - error_code = e.args[0] - error_msg = e.args[1] - elif len(e.args) == 1: - error_msg = e.args[0] - except Exception: - pass - - if not error_code: - error_text = f"Error Message: {error_msg}" - else: - error_text = f"Error Code: {error_code}. Error Message: {error_msg}" - if app_connector: - app_connector.error_print(f"{error_text}") - return error_text - - -def _validate_integer(action_result, parameter, key, allow_zero=False): - """ - Validate an integer. - - :param action_result: Action result or BaseConnector object - :param parameter: input parameter - :param key: input parameter message key - :allow_zero: whether zero should be considered as valid value or not - :return: status phantom.APP_ERROR/phantom.APP_SUCCESS, integer value of the parameter or None in case of failure - """ - if parameter is not None: - try: - if not float(parameter).is_integer(): - return ( - action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_VALID_INT_MSG.format(param=key)), - None, - ) - - parameter = int(parameter) - except Exception: - return ( - action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_VALID_INT_MSG.format(param=key)), - None, - ) - - if parameter < 0: - return action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_NON_NEG_NON_ZERO_INT_MSG.format(param=key)), None - - if not allow_zero and parameter == 0: - return ( - action_result.set_status( - phantom.APP_ERROR, - MSGOFFICE365_NON_NEG_NON_ZERO_INT_MSG.format(param=key), - ), - None, - ) - - return phantom.APP_SUCCESS, parameter - - -def _handle_oauth_result(request, path_parts): - """ - ?admin_consent=True&tenant=a417c578-c7ee-480d-a225-d48057e74df5&state=13 - """ - asset_id = request.GET.get("state") - if not asset_id: - return HttpResponse( - f"ERROR: Asset ID not found in URL\n{json.dumps(request.GET)}", - content_type="text/plain", - status=400, - ) - - # first check for error info - error = request.GET.get("error") - error_description = request.GET.get("error_description") - - if error: - msg = f"Error: {error}" - if error_description: - msg += f" Details: {error_description}" - return HttpResponse(f"Server returned {msg}", content_type="text/plain", status=400) - - admin_consent = request.GET.get("admin_consent") - code = request.GET.get("code") - - if not admin_consent and not code: - return HttpResponse( - f"ERROR: admin_consent or authorization code not found in URL\n{json.dumps(request.GET)}", - content_type="text/plain", - status=400, - ) - - # Load the data - state = _load_app_state(asset_id) - - if admin_consent: - if admin_consent == "True": - admin_consent = True - else: - admin_consent = False - - state["admin_consent"] = admin_consent - _save_app_state(state, asset_id) - - # If admin_consent is True - if admin_consent: - return HttpResponse( - "Admin Consent received. Please close this window.", - content_type="text/plain", - ) - return HttpResponse( - "Admin Consent declined. Please close this window and try again later.", - content_type="text/plain", - status=400, - ) - - # If value of admin_consent is not available, value of code is available - state["code"] = code - _save_app_state(state, asset_id) - - return HttpResponse( - "Code received. Please close this window, the action will continue to get new token.", - content_type="text/plain", - ) - - -def _handle_oauth_start(request, path_parts): - # get the asset id, the state file is created for each asset - asset_id = request.GET.get("asset_id") - if not asset_id: - return HttpResponse("ERROR: Asset ID not found in URL", content_type="text/plain", status=404) - - # Load the state that was created for the asset - state = _load_app_state(asset_id) - if not state: - return HttpResponse( - "ERROR: The asset ID is invalid or an error occurred while reading the state file", - content_type="text/plain", - status=400, - ) - - # get the url to point to the authorize url of OAuth - admin_consent_url = state.get("admin_consent_url") - - if not admin_consent_url: - return HttpResponse( - "App state is invalid, admin_consent_url key not found", - content_type="text/plain", - status=400, - ) - - # Redirect to this link, the user will then require to enter credentials interactively - response = HttpResponse(status=302) - response["Location"] = admin_consent_url - - return response - - -def handle_request(request, path_parts): - """ - request contains the data posted to the rest endpoint, it is the django http request object - path_parts is a list of the URL tokenized - """ - - # get the type of data requested, it's the last part of the URL used to post to the REST endpoint - if len(path_parts) < 2: - return HttpResponse( - "error: True, message: Invalid REST endpoint request", - content_type="text/plain", - status=404, - ) - - call_type = path_parts[1] - - if call_type == "start_oauth": - # start the authentication process - return _handle_oauth_start(request, path_parts) - - if call_type == "result": - # process the 'code' - ret_val = _handle_oauth_result(request, path_parts) - asset_id = request.GET.get("state") # nosemgrep - if asset_id and asset_id.isalnum(): - app_dir = os.path.dirname(os.path.abspath(__file__)) - auth_status_file_path = f"{app_dir}/{asset_id}_{TC_FILE}" - real_auth_status_file_path = os.path.abspath(auth_status_file_path) - if not os.path.dirname(real_auth_status_file_path) == app_dir: - return HttpResponse("Error: Invalid asset_id", content_type="text/plain", status=400) - open(auth_status_file_path, "w").close() - try: - uid = pwd.getpwnam("apache").pw_uid - gid = grp.getgrnam("phantom").gr_gid - os.chown(auth_status_file_path, uid, gid) - os.chmod(auth_status_file_path, "0664") - except Exception: - pass - - return ret_val - - return HttpResponse("error: Invalid endpoint", content_type="text/plain", status=404) - - -def _get_dir_name_from_app_name(app_name): - app_name = "".join([x for x in app_name if x.isalnum()]) - app_name = app_name.lower() - - if not app_name: - # hardcode it - app_name = "app_for_phantom" - - return app_name - - -class Office365Connector(BaseConnector): - def __init__(self): - # Call the BaseConnectors init first - super().__init__() - - self._state = None - - # Variable to hold a base_url in case the app makes REST calls - # Do note that the app json defines the asset config, so please - # modify this as you deem fit. - self._base_url = None - self._tenant = None - self._client_id = None - self._auth_type = None - self._client_secret = None - self._admin_access = None - self._scope = None - self._access_token = None - self._refresh_token = None - self._REPLACE_CONST = "C53CEA8298BD401BA695F247633D0542" # pragma: allowlist secret - self._duplicate_count = 0 - self._asset_id = None - self._cba_auth = None - self._private_key = None - self._certificate_private_key = None - - def load_state(self): - """ - Load the contents of the state file to the state dictionary and decrypt it. - - :return: loaded state - """ - state = super().load_state() - if not isinstance(state, dict): - self.debug_print("Resetting the state file with the default format") - state = {"app_version": self.get_app_json().get("app_version")} - return state - return self._decrypt_state(state) - - def save_state(self, state): - """ - Encrypt and save the current state dictionary to the state file. - - :param state: state dictionary - :return: status - """ - return super().save_state(self._encrypt_state(state)) - - def update_state_fields(self, value, helper_function, error_message): - try: - return helper_function(value, self._asset_id) - except Exception as ex: - self.debug_print(f"{error_message}: {_get_error_msg_from_exception(ex, self)}") - return None - - def check_state_fields(self, state, helper_function, error_message): - access_token = state.get("non_admin_auth", {}).get("access_token") - if access_token: - state["non_admin_auth"]["access_token"] = self.update_state_fields(access_token, helper_function, error_message) - refresh_token = state.get("non_admin_auth", {}).get("refresh_token") - if refresh_token: - state["non_admin_auth"]["refresh_token"] = self.update_state_fields(refresh_token, helper_function, error_message) - access_token = state.get("admin_auth", {}).get("access_token") - if access_token: - state["admin_auth"]["access_token"] = self.update_state_fields(access_token, helper_function, error_message) - return state - - def _decrypt_state(self, state): - """ - Decrypts the state. - - :param state: state dictionary - :return: decrypted state - """ - if not state.get("is_encrypted"): - return state - return self.check_state_fields(state, encryption_helper.decrypt, MSGOFFICE365_DECRYPTION_ERROR) - - def _encrypt_state(self, state): - """ - Encrypts the state. - - :param state: state dictionary - :return: encrypted state - """ - - state = self.check_state_fields(state, encryption_helper.encrypt, MSGOFFICE365_ENCRYPTION_ERROR) - state["is_encrypted"] = True - - return state - - def _dump_error_log(self, error, msg="Exception occurred."): - self.error_print(msg, dump_object=error) - - def _process_empty_response(self, response, action_result): - if response.status_code == 200: - return RetVal(phantom.APP_SUCCESS, {}) - return RetVal(action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_ERROR_EMPTY_RESPONSE.format(code=response.status_code)), None) - - def _process_html_response(self, response, action_result): - # An html response, treat it like an error - status_code = response.status_code - - try: - soup = BeautifulSoup(response.text, "html.parser") - # Remove the script, style, footer and navigation part from the HTML message - for element in soup(["script", "style", "footer", "nav"]): - element.extract() - error_text = soup.text - split_lines = error_text.split("\n") - split_lines = [x.strip() for x in split_lines if x.strip()] - error_text = "\n".join(split_lines) - except Exception: - error_text = "Cannot parse error details" - - msg = f"Status Code: {status_code}. Data from server:\n{error_text}\n" - - msg = msg.replace("{", "{{").replace("}", "}}") - - return RetVal(action_result.set_status(phantom.APP_ERROR, msg), None) - - def _process_json_response(self, r, action_result): - # Try a json parse - try: - resp_json = r.json() - except Exception as e: - error_msg = _get_error_msg_from_exception(e, self) - return RetVal(action_result.set_status(phantom.APP_ERROR, f"Unable to parse JSON response. {error_msg}"), None) - - # Please specify the status codes here - if 200 <= r.status_code < 399: - return RetVal(phantom.APP_SUCCESS, resp_json) - - try: - error_code = "" - error_text = "" - - error_msg = "" - error = resp_json.get("error", "") - error_desc = resp_json.get("error_description", "") - if isinstance(error, dict): - error_code = error.get("code") - error_msg = error.get("message") - - if error_msg: - try: - soup = BeautifulSoup(resp_json.get("error", {}).get("message"), "html.parser") - # Remove the script, style, footer and navigation part from the HTML message - for element in soup(["script", "style", "footer", "nav"]): - element.extract() - error_text = soup.text - split_lines = error_text.split("\n") - split_lines = [x.strip() for x in split_lines if x.strip()] - error_text = "\n".join(split_lines) - if len(error_text) > 500: - error_text = "Error while connecting to a server (Please check input parameters or asset configuration parameters)" - except Exception: - error_text = "Cannot parse error details" - - if error_code: - error_text = f"{error_code}. {error_text}" - - if error_desc: - error_text = f"{error_desc}. {error_text}" - - if not error_text: - error_text = r.text.replace("{", "{{").replace("}", "}}") - except Exception: - error_text = r.text.replace("{", "{{").replace("}", "}}") - - # You should process the error returned in the json - - msg = f"Error: Status Code: {r.status_code} Data from server: {error_text}" - - return RetVal(action_result.set_status(phantom.APP_ERROR, msg), None) - - def _process_response(self, r, action_result): - # store the r_text in debug data, it will get dumped in the logs if the action fails - if hasattr(action_result, "add_debug_data"): - action_result.add_debug_data({"r_status_code": r.status_code}) - action_result.add_debug_data({"r_text": r.text}) - action_result.add_debug_data({"r_headers": r.headers}) - - # Process each 'Content-Type' of response separately - - # Process a json response - content_type = r.headers.get("Content-Type", "") - if "json" in content_type or "javascript" in content_type: - return self._process_json_response(r, action_result) - - # Process an HTML response, Do this no matter what the api talks. - # There is a high chance of a PROXY in between Splunk SOAR and the rest of - # world, in case of errors, PROXY's return HTML, this function parses - # the error and adds it to the action_result. - if "html" in r.headers.get("Content-Type", ""): - return self._process_html_response(r, action_result) - - if r.status_code == 404: - return RetVal(action_result.set_status(phantom.APP_ERROR, "Email not found"), None) - - if 200 <= r.status_code <= 204: - return RetVal(phantom.APP_SUCCESS, None) - - # it's not content-type that is to be parsed, handle an empty response - if not r.text: - return self._process_empty_response(r, action_result) - - # everything else is actually an error at this point - - msg = "Can't process response from server. Status Code: {} Data from server: {}".format( - r.status_code, r.text.replace("{", "{{").replace("}", "}}") - ) - - return RetVal(action_result.set_status(phantom.APP_ERROR, msg), None) - - def _make_rest_call( - self, - action_result, - url, - verify=True, - headers={}, - params=None, - data=None, - method="get", - download=False, - ): - resp_json = None - - try: - request_func = getattr(requests, method) - except AttributeError: - return RetVal( - action_result.set_status(phantom.APP_ERROR, f"Invalid method: {method}"), - resp_json, - ) - - for _ in range(self._number_of_retries): - try: - r = request_func( - url, - data=data, - headers=headers, - verify=verify, - params=params, - timeout=MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT, - ) - except Exception as e: - error_msg = _get_error_msg_from_exception(e, self) - return RetVal(action_result.set_status(phantom.APP_ERROR, f"Error connecting to server. {error_msg}"), resp_json) - - if r.status_code != 502: - break - self.debug_print("Received 502 status code from the server") - time.sleep(self._retry_wait_time) - - if download: - if 200 <= r.status_code < 399: - return RetVal(phantom.APP_SUCCESS, r.text) - self.debug_print("Error while downloading a file content") - return RetVal(phantom.APP_ERROR, None) - - return self._process_response(r, action_result) - - def _get_asset_name(self, action_result): - rest_endpoint = SPLUNK_SOAR_ASSET_INFO_URL.format(url=self.get_phantom_base_url(), asset_id=self._asset_id) - - ret_val, resp_json = self._make_rest_call(action_result, rest_endpoint, False) - - if phantom.is_fail(ret_val): - return (ret_val, None) - - asset_name = resp_json.get("name") - - if not asset_name: - return action_result.set_status( - phantom.APP_ERROR, - f"Asset Name for ID: {self._asset_id} not found", - None, - ) - - return (phantom.APP_SUCCESS, asset_name) - - def _update_container(self, action_result, container_id, container): - """ - Update container. - - :param action_result: Action result or BaseConnector object - :param container_id: container ID - :param container: container's payload to update - :return: status phantom.APP_ERROR/phantom.APP_SUCCESS with status message - """ - rest_endpoint = SPLUNK_SOAR_CONTAINER_INFO_URL.format(url=self.get_phantom_base_url(), container_id=container_id) - - try: - data = json.dumps(container) - except Exception as e: - error_msg = _get_error_msg_from_exception(e, self) - msg = ( - f"json.dumps failed while updating the container: {container_id}. " - "Possibly a value in the container dictionary is not encoded properly." - f"Exception: {error_msg}" - ) - return action_result.set_status(phantom.APP_ERROR, msg) - - ret_val, _ = self._make_rest_call(action_result, rest_endpoint, False, data=data, method="post") - if phantom.is_fail(ret_val): - return action_result.get_status() - - return phantom.APP_SUCCESS - - def _get_phantom_base_url(self, action_result): - ret_val, resp_json = self._make_rest_call( - action_result, - SPLUNK_SOAR_SYS_INFO_URL.format(url=self.get_phantom_base_url()), - False, - ) - - if phantom.is_fail(ret_val): - return (ret_val, None) - - phantom_base_url = resp_json.get("base_url").rstrip("/") - - if not phantom_base_url: - return ( - action_result.set_status( - phantom.APP_ERROR, - "Splunk SOAR Base URL not found in System Settings. Please specify this value in System Settings", - ), - None, - ) - - return (phantom.APP_SUCCESS, phantom_base_url) - - def _get_url_to_app_rest(self, action_result=None): - if not action_result: - action_result = ActionResult() - - # get the Splunk SOAR ip to redirect to - ret_val, phantom_base_url = self._get_phantom_base_url(action_result) - - if phantom.is_fail(ret_val): - return (action_result.get_status(), None) - - # get the asset name - ret_val, asset_name = self._get_asset_name(action_result) - - if phantom.is_fail(ret_val): - return (action_result.get_status(), None) - - self.save_progress(f"Using Splunk SOAR base URL as: {phantom_base_url}") - - app_json = self.get_app_json() - - app_name = app_json["name"] - - app_dir_name = _get_dir_name_from_app_name(app_name) - - url_to_app_rest = "{}/rest/handler/{}_{}/{}".format(phantom_base_url, app_dir_name, app_json["appid"], asset_name) - - return (phantom.APP_SUCCESS, url_to_app_rest) - - def _make_rest_call_helper( - self, - action_result, - endpoint, - verify=True, - headers=None, - params=None, - data=None, - method="get", - nextLink=None, - download=False, - beta=False, - ): - if nextLink: - url = nextLink - else: - if not beta: - url = f"{MSGRAPH_API_URL}/v1.0{endpoint}" - else: - url = f"{MSGRAPH_API_URL}/beta{endpoint}" - - if headers is None: - headers = {} - - headers.update({"Authorization": f"Bearer {self._access_token}", "Accept": "application/json", "Content-Type": "application/json"}) - - ret_val, resp_json = self._make_rest_call(action_result, url, verify, headers, params, data, method, download=download) - - # If token is expired, generate a new token - msg = action_result.get_message() - if msg and (("token" in msg and "expired" in msg) or any(failure_msg in msg for failure_msg in MSGOFFICE365_AUTH_FAILURE_MSG)): - self.debug_print("MSGRAPH", f"Error '{msg}' found in API response. Requesting new access token using refresh token") - ret_val = self._get_token(action_result) - if phantom.is_fail(ret_val): - return action_result.get_status(), None - - headers.update({"Authorization": f"Bearer {self._access_token}"}) - - ret_val, resp_json = self._make_rest_call( - action_result, - url, - verify, - headers, - params, - data, - method, - download=download, - ) - - if phantom.is_fail(ret_val): - return action_result.get_status(), None - - return phantom.APP_SUCCESS, resp_json - - def _sanitize_file_name(self, file_name): - return re.sub("[,\"']", "", file_name) - - def _add_attachment_to_vault(self, attachment, container_id, file_data): - fd, tmp_file_path = tempfile.mkstemp(dir=Vault.get_vault_tmp_dir()) - os.close(fd) - file_mode = "wb" if isinstance(file_data, bytes) else "w" - with open(tmp_file_path, file_mode) as f: - f.write(file_data) - - file_name = self._sanitize_file_name(attachment["name"]) - - success, msg, vault_id = ph_rules.vault_add( - container=container_id, - file_location=tmp_file_path, - file_name=file_name, - ) - if not success: - self.debug_print(f"Error adding file to vault: {msg}") - return RetVal(phantom.APP_ERROR, None) - else: - return RetVal(phantom.APP_SUCCESS, vault_id) - - def _handle_attachment(self, attachment, container_id, artifact_json=None): - vault_id = None - - try: - if "contentBytes" in attachment: # Check whether the attachment contains the data - file_data = base64.b64decode(attachment.pop("contentBytes")) - ret_val, vault_id = self._add_attachment_to_vault(attachment, container_id, file_data) - if phantom.is_fail(ret_val): - return phantom.APP_ERROR - else: - self.debug_print("No content found in the attachment. Hence, skipping the vault file creation.") - - except Exception as e: - error_msg = _get_error_msg_from_exception(e, self) - self.debug_print(f"Error saving file to vault: {error_msg}") - return phantom.APP_ERROR - - if artifact_json is None: - attachment["vaultId"] = vault_id - return phantom.APP_SUCCESS - - artifact_json["name"] = "Vault Artifact" - artifact_json["label"] = "attachment" - artifact_json["container_id"] = container_id - artifact_json["source_data_identifier"] = attachment["id"] - - artifact_cef = {} - - artifact_cef["size"] = attachment["size"] - artifact_cef["lastModified"] = attachment["lastModifiedDateTime"] - artifact_cef["filename"] = attachment["name"] - artifact_cef["mimeType"] = attachment["contentType"] - if vault_id: - artifact_cef["vault_id"] = vault_id - - artifact_json["cef"] = artifact_cef - - return phantom.APP_SUCCESS - - def _handle_item_attachment(self, attachment, container_id, endpoint, action_result): - vault_id = None - - try: - attach_endpoint = "{}/{}/$value".format(endpoint, attachment["id"]) - ret_val, rfc822_email = self._make_rest_call_helper(action_result, attach_endpoint, download=True) - if phantom.is_fail(ret_val): - self.debug_print("Error while downloading the file content, for attachment id: {}".format(attachment["id"])) - return phantom.APP_ERROR - - attachment["name"] = "{}.eml".format(attachment["name"]) - - if rfc822_email: # Check whether the API returned any data - ret_val, vault_id = self._add_attachment_to_vault(attachment, container_id, rfc822_email) - if phantom.is_fail(ret_val): - return phantom.APP_ERROR - else: - self.debug_print("No content found for the item attachment. Hence, skipping the vault file creation.") - - except Exception as e: - error_msg = _get_error_msg_from_exception(e, self) - self.debug_print(f"Error saving file to vault: {error_msg}") - return phantom.APP_ERROR - - attachment["vaultId"] = vault_id - return phantom.APP_SUCCESS - - def _create_reference_attachment_artifact(self, container_id, attachment, artifact_json): - """ - Create reference attachment artifact. - - :param container_id: container ID - :param attachment: attachment dict - :param artifact_json: artifact dict to add the data - :return: phantom.APP_SUCCESS - """ - artifact_json["name"] = "Reference Attachment Artifact" - artifact_json["container_id"] = container_id - artifact_json["source_data_identifier"] = attachment["id"] - - artifact_cef = {} - - artifact_cef["size"] = attachment.get("size") - artifact_cef["lastModified"] = attachment.get("lastModifiedDateTime") - artifact_cef["filename"] = attachment.get("name") - artifact_cef["mimeType"] = attachment.get("contentType") - - artifact_json["cef"] = artifact_cef - - return phantom.APP_SUCCESS - - def _create_email_artifacts(self, container_id, email, artifact_id=None, create_iocs=True): - """ - Create email artifacts. - - :param container_id: container ID - :param email: email content - :param artifact_id: artifact ID - :return: extracted artifacts list - """ - artifacts = [] - - email_artifact = {} - email_artifact["label"] = "email" - email_artifact["name"] = "Email Artifact" - email_artifact["container_id"] = container_id - - if email.get("id"): - artifact_id = email["id"] - - # Set email ID contains - self._process_email._set_email_id_contains(email["id"]) - email_artifact["cef_types"] = {"messageId": self._process_email._email_id_contains} - - email_artifact["source_data_identifier"] = artifact_id - - cef = {} - email_artifact["cef"] = cef - - for k, v in email.items(): - if v is not None: - if k == "from": - from_obj = v.get("emailAddress", {}) - cef[k] = from_obj - cef["fromEmail"] = from_obj.get("address", "") - elif k == "toRecipients": - cef[k] = v - # add first email to To - recipients = v - if len(recipients): - cef["toEmail"] = recipients[0].get("emailAddress", {}).get("address", "") - elif k == "id": - cef["messageId"] = v - elif k == "internetMessageHeaders": - cef["internetMessageHeaders"] = {} - if isinstance(v, list): - for header in v: - key_name = header.get("name") - key_value = header.get("value") - if key_name and key_value: - cef["internetMessageHeaders"][key_name] = key_value - elif k == "attachments": - continue - else: - cef[k] = v - - if cef.get("body", {}).get("content") and (cef.get("body", {}).get("contentType") == "html"): - html_body = cef["body"]["content"] - - try: - soup = BeautifulSoup(html_body, "html.parser") - # Remove the script, style, footer, title and navigation part from the HTML message - for element in soup(["script", "style", "footer", "title", "nav"]): - element.extract() - body_text = soup.get_text(separator=" ") - split_lines = body_text.split("\n") - split_lines = [x.strip() for x in split_lines if x.strip()] - body_text = "\n".join(split_lines) - if body_text: - cef["bodyText"] = body_text - except Exception: - self.debug_print("Cannot parse email body text details") - - if not create_iocs: - return [email_artifact] - - body = email["body"]["content"] - - ips = [] - self._process_email._get_ips(body, ips) - - for ip in ips: - ip_artifact = {} - artifacts.append(ip_artifact) - ip_artifact["name"] = "IP Artifact" - ip_artifact["label"] = "artifact" - ip_artifact["cef"] = ip - ip_artifact["container_id"] = container_id - ip_artifact["source_data_identifier"] = artifact_id - - urls = [] - domains = [] - self._process_email._extract_urls_domains(body, urls, domains) - - for url in urls: - url_artifact = {} - artifacts.append(url_artifact) - url_artifact["name"] = "URL Artifact" - url_artifact["label"] = "artifact" - url_artifact["cef"] = url - url_artifact["container_id"] = container_id - url_artifact["source_data_identifier"] = artifact_id - - for domain in domains: - domain_artifact = {} - artifacts.append(domain_artifact) - domain_artifact["name"] = "Domain Artifact" - domain_artifact["label"] = "artifact" - domain_artifact["cef"] = domain - domain_artifact["container_id"] = container_id - domain_artifact["source_data_identifier"] = artifact_id - - hashes = [] - self._process_email._extract_hashes(body, hashes) - - for hash in hashes: - hash_artifact = {} - artifacts.append(hash_artifact) - hash_artifact["name"] = "Hash Artifact" - hash_artifact["label"] = "artifact" - hash_artifact["cef"] = hash - hash_artifact["container_id"] = container_id - hash_artifact["source_data_identifier"] = artifact_id - - artifacts.append(email_artifact) - - return artifacts - - def _extract_attachments( - self, - config, - attach_endpoint, - artifacts, - action_result, - attachments, - container_id, - first_time=False, - ): - """ - Extract attachments. - - :param config: config dict - :param attach_endpoint: attachment endpoint - :param artifacts: artifacts list to append the attachment artifacts - :param action_result: Action result or BaseConnector object - :param attachments: attachments list to process - :param container_id: container ID - :param first_time: boolean flag to specify if we want to expand the item attachment - :return: status phantom.APP_ERROR/phantom.APP_SUCCESS with status message - """ - for attachment in attachments: - if attachment.get("@odata.type") == "#microsoft.graph.itemAttachment": - # We need to expand the item attachment only once - if first_time: - sub_email_endpoint = attach_endpoint + "/{}?$expand=microsoft.graph.itemattachment/item".format(attachment["id"]) - ret_val, sub_email_resp = self._make_rest_call_helper(action_result, sub_email_endpoint) - if phantom.is_fail(ret_val): - return action_result.get_status() - sub_email = sub_email_resp.get("item", {}) - - else: - sub_email = attachment.get("item", {}) - - if sub_email: - sub_artifacts = self._create_email_artifacts(container_id, sub_email, attachment["id"], create_iocs=False) - artifacts += sub_artifacts - - # Use recursive approach to extract the reference attachment - item_attachments = sub_email.pop("attachments", []) - if item_attachments: - ret_val = self._extract_attachments( - config, - attach_endpoint, - artifacts, - action_result, - item_attachments, - container_id, - ) - if phantom.is_fail(ret_val): - self.debug_print("Error while processing nested attachments, for attachment id: {}".format(attachment["id"])) - - if first_time: - # Fetch the rfc822 content for the item attachment - sub_email_endpoint = "{}/{}/$value".format(attach_endpoint, attachment["id"]) - attachment["name"] = "{}.eml".format(attachment["name"]) - ret_val, rfc822_email = self._make_rest_call_helper(action_result, sub_email_endpoint, download=True) - if phantom.is_fail(ret_val): - self.debug_print("Error while downloading the email content, for attachment id: {}".format(attachment["id"])) - - if rfc822_email: - # Create ProcessEmail Object for email item attachment - process_email_obj = ProcessEmail(self, config) - process_email_obj._trigger_automation = False - - ret_val, msg = process_email_obj.process_email( - rfc822_email, attachment["id"], epoch=None, container_id=container_id, ingest_email=False - ) - - if phantom.is_fail(ret_val): - self.debug_print("Error while processing the email content, for attachment id: {}".format(attachment["id"])) - - if config.get("ingest_eml", False): - # Add eml file into the vault if ingest_email is checked - ret_val, vault_id = self._add_attachment_to_vault(attachment, container_id, rfc822_email) - if phantom.is_fail(ret_val): - self.debug_print("Could not process item attachment. See logs for details") - else: - # If success, create vault artifact - artifact_json = { - "name": "Vault Artifact", - "label": "attachment", - "container_id": container_id, - "source_data_identifier": attachment["id"], - } - - artifact_cef = { - "size": attachment["size"], - "lastModified": attachment["lastModifiedDateTime"], - "filename": attachment["name"], - "mimeType": attachment["contentType"], - } - if vault_id: - artifact_cef["vault_id"] = vault_id - artifact_json["cef"] = artifact_cef - artifacts.append(artifact_json) - - else: - self.debug_print("No content found for the item attachment. Hence, skipping the email file processing.") - - elif attachment.get("@odata.type") == "#microsoft.graph.referenceAttachment": - attach_artifact = {} - artifacts.append(attach_artifact) - self._create_reference_attachment_artifact(container_id, attachment, attach_artifact) - - elif attachment.get("name", "").endswith(".eml"): - if "contentBytes" in attachment: - try: - rfc822_email = base64.b64decode(attachment["contentBytes"]) - rfc822_email = UnicodeDammit(rfc822_email).unicode_markup - except Exception as e: - error_msg = _get_error_msg_from_exception(e, self) - self.debug_print(f"Unable to decode Email Mime Content. {error_msg}") - return action_result.set_status(phantom.APP_ERROR, "Unable to decode Email Mime Content") - - # Create ProcessEmail Object for email file attachment - process_email_obj = ProcessEmail(self, config) - process_email_obj._trigger_automation = False - - ret_val, msg = process_email_obj.process_email(rfc822_email, attachment["id"], epoch=None, container_id=container_id) - - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, msg) - else: - self.debug_print("No content found in the .eml file attachment. Hence, skipping the email file processing.") - - elif first_time: - attach_artifact = {} - artifacts.append(attach_artifact) - if not self._handle_attachment(attachment, container_id, artifact_json=attach_artifact): - return action_result.set_status( - phantom.APP_ERROR, - "Could not process attachment. See logs for details.", - ) - - return phantom.APP_SUCCESS - - def _process_email_data(self, config, action_result, endpoint, email): - """ - Process email data. - - :param config: config dict - :param action_result: Action result or BaseConnector object - :param endpoint: endpoint for making REST calls - :param emails: Emails to process - :return: status phantom.APP_ERROR/phantom.APP_SUCCESS with status message - """ - container = {} - - container["name"] = email["subject"] if email["subject"] else email["id"] - container_description = MSGOFFICE365_CONTAINER_DESCRIPTION.format(last_modified_time=email["lastModifiedDateTime"]) - container["description"] = container_description - container["source_data_identifier"] = email["id"] - container["data"] = {"raw_email": email} - - ret_val, msg, container_id = self.save_container(container) - - if phantom.is_fail(ret_val) or not container_id: - return action_result.set_status(phantom.APP_ERROR, msg) - - if MSGOFFICE365_DUPLICATE_CONTAINER_FOUND_MSG in msg.lower(): - self.debug_print("Duplicate container found") - self._duplicate_count += 1 - - # Prevent further processing if the email is not modified - ret_val, container_info, status_code = self.get_container_info(container_id=container_id) - if phantom.is_fail(ret_val): - return action_result.set_status( - phantom.APP_ERROR, - f"Status Code: {status_code}. Error occurred while fetching the container info for container ID: {container_id}", - ) - - if container_info.get("description", "") == container_description: - msg = "Email ID: {} has not been modified. Hence, skipping the artifact ingestion.".format(email["id"]) - self.debug_print(msg) - return action_result.set_status(phantom.APP_SUCCESS, msg) - else: - # Update the container's description and continue - self.debug_print("Updating container's description") - ret_val = self._update_container(action_result, container_id, container) - if phantom.is_fail(ret_val): - return action_result.get_status() - - self.debug_print("Creating email artifacts") - email_artifacts = self._create_email_artifacts(container_id, email) - attachment_artifacts = [] - - if config.get("extract_eml", True): - subject = email.get("subject") - email_message = { - "id": email["id"], - "name": subject if subject else "email_message_{}".format(email["id"]), - } - if not self._handle_item_attachment( - email_message, - container_id, - "/users/{}/messages".format(config.get("email_address")), - action_result, - ): - return action_result.set_status( - phantom.APP_ERROR, - "Could not download the email. See logs for details", - ) - email["vaultId"] = email_message["vaultId"] - - artifact_json = { - "name": "Vault Artifact", - "label": "Email Attachment", - "container_id": container_id, - "source_data_identifier": email["id"], - } - - artifact_cef = { - "filename": "{}.eml".format(email_message["name"]), - } - - if email_message["vaultId"]: - artifact_cef.update({"vaultId": email_message["vaultId"]}) - artifact_json["cef"] = artifact_cef - attachment_artifacts.append(artifact_json) - - if email["hasAttachments"] and config.get("extract_attachments", False): - attach_endpoint = endpoint + "/{}/attachments".format(email["id"]) - ret_val, attach_resp = self._make_rest_call_helper(action_result, attach_endpoint) - if phantom.is_fail(ret_val): - return action_result.get_status() - - ret_val = self._extract_attachments( - config, - attach_endpoint, - attachment_artifacts, - action_result, - attach_resp.get("value", []), - container_id, - first_time=True, - ) - if phantom.is_fail(ret_val): - return action_result.get_status() - - artifacts = attachment_artifacts + email_artifacts - ret_val, msg, container_id = self.save_artifacts(artifacts) - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, msg) - - return phantom.APP_SUCCESS - - def _process_email_details( - self, action_result, email, email_address, endpoint, extract_headers=False, download_attachments=False, download_email=False - ): - """ - Process email details including headers, attachments and email downloads. - - :param action_result: Action result object for tracking status - :param email: Email object to process - :param email_address: Email address of the mailbox - :param endpoint: Base endpoint for API calls - :param extract_headers: Whether to extract email headers (default: False) - :param download_attachments: Whether to download attachments (default: False) - :param download_email: Whether to download the email as EML (default: False) - :return: Updated email object with additional details including: - - internetMessageHeaders: Flattened email headers if extract_headers=True - - attachments: List of processed attachments if download_attachments=True - - vaultId: Vault ID of downloaded EML file if download_email=True - """ - if extract_headers: - header_endpoint = endpoint + "?$select=internetMessageHeaders" - ret_val, header_response = self._make_rest_call_helper(action_result, header_endpoint) - - if phantom.is_fail(ret_val): - return action_result.get_status() - # For Drafts there might not be any internetMessageHeaders, - # so we have to use get() fetching instead of directly fetching from dictionary - email["internetMessageHeaders"] = header_response.get("internetMessageHeaders") - - if download_attachments and email.get("hasAttachments"): - endpoint += "/attachments" - attachment_endpoint = f"{endpoint}?$expand=microsoft.graph.itemattachment/item" - ret_val, attach_resp = self._make_rest_call_helper(action_result, attachment_endpoint) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - for attachment in attach_resp.get("value", []): - # If it is fileAttachment, then we have to ingest it - if attachment.get("@odata.type") == "#microsoft.graph.fileAttachment": - if not self._handle_attachment(attachment, self.get_container_id()): - return action_result.set_status( - phantom.APP_ERROR, - "Could not process attachment. See logs for details", - ) - elif attachment.get("@odata.type") == "#microsoft.graph.itemAttachment": - if not self._handle_item_attachment(attachment, self.get_container_id(), endpoint, action_result): - return action_result.set_status( - phantom.APP_ERROR, - "Could not process item attachment. See logs for details", - ) - - email["attachments"] = attach_resp["value"] - - if email.get("@odata.type") in [ - "#microsoft.graph.eventMessage", - "#microsoft.graph.eventMessageRequest", - "#microsoft.graph.eventMessageResponse", - ]: - event_endpoint = f"{endpoint}/?$expand=Microsoft.Graph.EventMessage/Event" - ret_val, event_resp = self._make_rest_call_helper(action_result, event_endpoint) - if phantom.is_fail(ret_val): - return action_result.get_status() - - email["event"] = event_resp["event"] - - if "internetMessageHeaders" in email: - email["internetMessageHeaders"] = self._flatten_headers(email["internetMessageHeaders"]) - - # If the response has attachments, update every attachment data with its type - # 'attachmentType' key - indicates type of attachment - # and if an email has any itemAttachment, then also add itemType in the response - # 'itemType' key - indicates type of itemAttachment - if email.get("attachments", []): - for attachment in email["attachments"]: - attachment_type = attachment.get("@odata.type", "") - attachment["attachmentType"] = attachment_type - if attachment_type == "#microsoft.graph.itemAttachment": - attachment["itemType"] = attachment.get("item", {}).get("@odata.type", "") - - if download_email: - subject = email.get("subject") - email_message = { - "id": email["id"], - "name": subject if subject else "email_message_{}".format(email["id"]), - } - if not self._handle_item_attachment( - email_message, - self.get_container_id(), - f"/users/{email_address}/messages", - action_result, - ): - return action_result.set_status( - phantom.APP_ERROR, - "Could not download the email. See logs for details", - ) - email["vaultId"] = email_message["vaultId"] - - return email - - def _remove_tokens(self, action_result): - # checks whether the message includes any of the known error codes - - if len(list(filter(lambda x: x in action_result.get_message(), MSGOFFICE365_ASSET_PARAM_CHECK_LIST_ERROR))) > 0: - if not self._admin_access: - if self._state.get("non_admin_auth", {}).get("access_token"): - self._state["non_admin_auth"].pop("access_token") - if self._state.get("non_admin_auth", {}).get("refresh_token"): - self._state["non_admin_auth"].pop("refresh_token") - else: - if self._state.get("admin_auth", {}).get("access_token"): - self._state["admin_auth"].pop("access_token") - - def _handle_test_connectivity(self, param): - """Function that handles the test connectivity action, it is much simpler than other action handlers.""" - - action_result = self.add_action_result(ActionResult(param)) - - # Get Consent in OAuth Authentication and it's requires Client Secret (Scenario - Automatic) - if self._auth_type != "cba" and self._client_secret and not (self._admin_access and self._admin_consent): - ret_val = self._get_consent(action_result) - if phantom.is_fail(ret_val): - if self._auth_type == "oauth": - return action_result.get_status() - else: - self._auth_type = "cba" - self.save_progress("Failed to obtain consent, switching to Certificate Based Authentication") - if not (self._thumbprint and self._certificate_private_key): - return action_result.set_status( - phantom.APP_ERROR, - "Tried switching to Certificate Based Authentication, but the necessary CBA configuraiton parameters are not set.", - ) - - self.save_progress("Getting the token") - ret_val = self._get_token(action_result) - - if phantom.is_fail(ret_val): - self._remove_tokens(action_result) - self.save_progress("Test Connectivity Failed") - return action_result.get_status() - - params = {"$top": "1"} - message_failed = "" - - # Application permissions are not supported when using the /me endpoint. (Scenario - CBA using /users) - if self._admin_access or self._state["auth_type"] == "cba": - message_failed = "API to fetch details of all the users failed" - self.save_progress("Getting info about all users to verify token") - ret_val, response = self._make_rest_call_helper(action_result, "/users", params=params) - else: - message_failed = "API to get user details failed" - self.save_progress("Getting info about a single user to verify token") - ret_val, response = self._make_rest_call_helper(action_result, "/me", params=params) - - if phantom.is_fail(ret_val): - self.save_progress(message_failed) - self.save_progress("Test Connectivity Failed") - return action_result.set_status(phantom.APP_ERROR) - - value = response.get("value") - - if value: - self.save_progress("Got user info") - - self.save_progress("Test Connectivity Passed") - - return action_result.set_status(phantom.APP_SUCCESS) - - def _handle_copy_email(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email_addr = param["email_address"] - folder = param["folder"] - message_id = param["id"] - endpoint = f"/users/{email_addr}" - - endpoint += f"/messages/{message_id}/copy" - - body = {"DestinationId": folder} - - if param.get("get_folder_id", True): - try: - dir_id, error, _ = self._get_folder_id(action_result, folder, email_addr) - except ReturnException as e: - self._dump_error_log(e) - return action_result.get_status() - - if dir_id: - body["DestinationId"] = dir_id - else: - self.save_progress(error) - return action_result.set_status(phantom.APP_ERROR, error) - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, data=json.dumps(body), method="post") - if phantom.is_fail(ret_val): - return action_result.get_status() - - action_result.add_data(response) - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully copied email") - - def _handle_move_email(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email_addr = param["email_address"] - folder = param["folder"] - message_id = param["id"] - endpoint = f"/users/{email_addr}" - - endpoint += f"/messages/{message_id}/move" - - body = {"DestinationId": folder} - if param.get("get_folder_id", True): - try: - dir_id, error, _ = self._get_folder_id(action_result, folder, email_addr) - - except ReturnException as e: - self._dump_error_log(e) - return action_result.get_status() - - if dir_id: - body["DestinationId"] = dir_id - - else: - self.save_progress(error) - return action_result.set_status(phantom.APP_ERROR, error) - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, data=json.dumps(body), method="post") - if phantom.is_fail(ret_val): - return action_result.get_status() - - action_result.add_data(response) - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully moved email") - - def _handle_delete_email(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email_addr = param["email_address"] - message_id = param["id"] - endpoint = f"/users/{email_addr}" - - endpoint += f"/messages/{message_id}" - - ret_val, _ = self._make_rest_call_helper(action_result, endpoint, method="delete") - if phantom.is_fail(ret_val): - return action_result.get_status() - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully deleted email") - - def _handle_delete_event(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email_addr = param["email_address"] - message_id = param["id"] - send_decline_response = param.get("send_decline_response") - endpoint = f"/users/{email_addr}/events/{message_id}" - method = "delete" - data = None - if send_decline_response: - method = "post" - endpoint += "/decline" - data = json.dumps({"sendResponse": True}) - - ret_val, _ = self._make_rest_call_helper(action_result, endpoint, method=method, data=data) - if phantom.is_fail(ret_val): - return action_result.get_status() - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully deleted event") - - def _handle_oof_check(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - user_id = param["user_id"] - - endpoint = f"/users/{user_id}/mailboxSettings/automaticRepliesSetting" - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, method="get") - if phantom.is_fail(ret_val): - return action_result.get_status() - - action_result.add_data(response) - - action_result.update_summary({"events_matched": action_result.get_data_size()}) - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully retrieved out of office status") - - def _handle_list_events(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - user_id = param.get("user_id") if param.get("user_id") else None - group_id = param.get("group_id") if param.get("group_id") else None - query = param.get("filter") if param.get("filter") else None - limit = param.get("limit") - - # Integer validation for 'limit' action parameter - ret_val, limit = _validate_integer(action_result, limit, "'limit' action") - if phantom.is_fail(ret_val): - return action_result.get_status() - - if user_id is None and group_id is None: - return action_result.set_status( - phantom.APP_ERROR, - 'Either a "user_id" or "group_id" must be supplied to the "list_events" action', - ) - - if user_id and group_id and user_id != "" and group_id != "": - return action_result.set_status( - phantom.APP_ERROR, - 'Either a user_id or group_id can be supplied to the "list_events" action - not both', - ) - - endpoint = "" - - if user_id: - endpoint = f"/users/{user_id}/calendar/events" - else: - endpoint = f"/groups/{group_id}/calendar/events" - - if query: - endpoint = f"{endpoint}?{query}" - - ret_val, events = self._paginator(action_result, endpoint, limit) - - if phantom.is_fail(ret_val): - msg = action_result.get_message() - if "$top" in msg or "$top/top" in msg: - msg += "The '$top' parameter is already used internally to handle pagination logic. " - msg += "If you want to restrict results in terms of number of output results, you can use the 'limit' parameter." - return action_result.set_status(phantom.APP_ERROR, msg) - return action_result.get_status() - - if not events: - # No events found is a valid scenario that there can be 0 events returned - # even if the API call is a success for the correct given inputs and hence, returning APP_SUCCESS. - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - for event in events: - attendees = [attendee.get("emailAddress", {}).get("name") for attendee in event.get("attendees", [])] - event["attendee_list"] = ", ".join(attendees) - action_result.add_data(event) - - num_events = len(events) - action_result.update_summary({"events_matched": action_result.get_data_size()}) - - return action_result.set_status( - phantom.APP_SUCCESS, - "Successfully retrieved {} event{}".format(num_events, "" if num_events == 1 else "s"), - ) - - def _handle_list_groups(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - limit = param.get("limit") - - # Integer validation for 'limit' action parameter - ret_val, limit = _validate_integer(action_result, limit, "'limit' action") - if phantom.is_fail(ret_val): - return action_result.get_status() - - query = param.get("filter") if param.get("filter") else None - - endpoint = "/groups" - - ret_val, groups = self._paginator(action_result, endpoint, limit, query=query) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - if not groups: - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - for group in groups: - action_result.add_data(group) - - num_groups = len(groups) - action_result.update_summary({"total_groups_returned": num_groups}) - - return action_result.set_status( - phantom.APP_SUCCESS, - "Successfully retrieved {} group{}".format(num_groups, "" if num_groups == 1 else "s"), - ) - - def _handle_list_group_members(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - limit = param.get("limit") - query = None - is_advance_query = False - - # Integer validation for 'limit' action parameter - ret_val, limit = _validate_integer(action_result, limit, "'limit' action") - if phantom.is_fail(ret_val): - return action_result.get_status() - - method = param.get("method", "Group ID") - group_id = identificator = param["identificator"] - if param.get("filter"): - query = param.get("filter") - is_advance_query = True - - if method.lower() not in ("group id", "group e-mail"): - return action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_INVALID_METHOD) - - if method.lower() == "group e-mail": - if not util.is_email(identificator): - return action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_INVALID_EMAIL) - - name_filtering = f"mail eq '{identificator}'" - ret_val, group = self._paginator(action_result, "/groups", limit, query=name_filtering) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - if not group: - return action_result.set_status( - phantom.APP_ERROR, f"There is no such {identificator} group name, Please check the correct spelling or existence" - ) - group_id = group[0]["id"] - - transitive_members = param.get("get_transitive_members", True) - endpoint = f"/groups/{group_id}/members" - if transitive_members: - endpoint = f"/groups/{group_id}/transitiveMembers" - - ret_val, members = self._paginator(action_result, endpoint, limit, query=query, is_advance_query=is_advance_query) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - if not members: - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - for member in members: - action_result.add_data(member) - - num_members = len(members) - action_result.update_summary({"total_members_returned": num_members}) - - return action_result.set_status( - phantom.APP_SUCCESS, - "Successfully retrieved {} group member{}".format(num_members, "" if num_members == 1 else "s"), - ) - - def _handle_list_users(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - limit = param.get("limit") - - # Integer validation for 'limit' action parameter - ret_val, limit = _validate_integer(action_result, limit, "'limit' action") - if phantom.is_fail(ret_val): - return action_result.get_status() - - query = param.get("filter") if param.get("filter") else None - - endpoint = "/users" - - ret_val, users = self._paginator(action_result, endpoint, limit, query=query) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - if not users: - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - for user in users: - action_result.add_data(user) - - num_users = len(users) - action_result.update_summary({"total_users_returned": num_users}) - - return action_result.set_status( - phantom.APP_SUCCESS, - "Successfully retrieved {} user{}".format(num_users, "" if num_users == 1 else "s"), - ) - - def _handle_list_rules(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - user_id = param["user_id"] - - endpoint = f"/users/{user_id}/mailFolders/inbox/messageRules" - - ret_val, rules = self._paginator(action_result, endpoint) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - if not rules: - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - for rule in rules: - action_result.add_data(rule) - - num_rules = len(rules) - action_result.update_summary({"total_rules_returned": num_rules}) - - return action_result.set_status( - phantom.APP_SUCCESS, - "Successfully retrieved {} rule{}".format(num_rules, "" if num_rules == 1 else "s"), - ) - - def flatten_json(self, y): - out = {} - - def flatten(x, name=""): - if type(x) is dict: - for a in x: - flatten(x[a], name + a + "_") - elif type(x) is list: - i = 0 - for a in x: - flatten(a, name + str(i) + "_") - i += 1 - else: - out[name[:-1]] = x - - flatten(y) - return out - - def _handle_get_rule(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - user_id = param["user_id"] - rule_id = param["rule_id"] - - endpoint = f"/users/{user_id}/mailFolders/inbox/messageRules/{rule_id}" - - ret_val, rule = self._make_rest_call_helper(action_result, endpoint) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - if not rule: - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - rule = self.flatten_json(rule) - self.debug_print(rule) - action_result.add_data(rule) - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully retrieved specified inbox rule") - - def _handle_list_folders(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - list_folder = list() - user_id = param["user_id"] - folder_id = param.get("folder_id") - - if not folder_id: - # fetching root level folders - ret_val, root_folders = self._fetch_root_folders(action_result, user_id) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - # adding root folders to main list of folders - list_folder.extend(root_folders) - - # checking for child folder if have, add it in list of folders - for root_folder in root_folders: - if root_folder.get("childFolderCount", 0) == 0: - continue - else: - ret_val = self._list_child_folders( - action_result, - list_folder, - user_id=user_id, - parent_folder=root_folder, - ) - - if phantom.is_fail(ret_val): - return action_result.get_status() - else: - ret_val = self._list_child_folders(action_result, list_folder, user_id=user_id, folder_id=folder_id) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - for folder in list_folder: - action_result.add_data(folder) - - num_folders = len(list_folder) - action_result.update_summary({"total_folders_returned": num_folders}) - - return action_result.set_status( - phantom.APP_SUCCESS, - "Successfully retrieved {} mail folder{}".format(num_folders, "" if num_folders == 1 else "s"), - ) - - def _fetch_root_folders(self, action_result, user_id): - endpoint = f"/users/{user_id}/mailFolders" - - ret_val, folders = self._paginator(action_result, endpoint) - - if phantom.is_fail(ret_val): - return action_result.get_status(), None - - if not folders: - return ( - action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND), - None, - ) - - return phantom.APP_SUCCESS, folders - - def _list_child_folders(self, action_result, list_folder, user_id, parent_folder=None, folder_id=None): - # fetching root level folders - if not folder_id: - ret_val, child_folders = self._fetch_child_folders(action_result, user_id, parent_folder["id"]) - else: - ret_val, child_folders = self._fetch_child_folders(action_result, user_id, folder_id) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - # checking for child folder if have, add it in list of folders - for child_folder in child_folders: - if child_folder.get("childFolderCount", 0) == 0: - list_folder.append(child_folder) - else: - ret_val = self._list_child_folders( - action_result, - list_folder, - user_id=user_id, - parent_folder=child_folder, - ) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - list_folder.append(child_folder) - - return phantom.APP_SUCCESS - - def _fetch_child_folders(self, action_result, user_id, folder_id): - endpoint = f"/users/{user_id}/mailFolders/{folder_id}/childFolders" - - ret_val, folders = self._paginator(action_result, endpoint) - - if phantom.is_fail(ret_val): - return action_result.get_status(), None - - return phantom.APP_SUCCESS, folders - - def _flatten_headers(self, headers): - new_headers = {} - if not headers: - return new_headers - - for field in headers: - if field["name"] == "Received": - if "Received" not in new_headers: - new_headers["Received"] = [] - new_headers["Received"].append(field["value"]) - continue - - new_headers[field["name"]] = field["value"] - - return new_headers - - def _handle_get_email(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email_addr = param["email_address"] - message_id = param["id"] - endpoint = f"/users/{email_addr}" - - endpoint += f"/messages/{message_id}" - - ret_val, response = self._make_rest_call_helper(action_result, endpoint) - if phantom.is_fail(ret_val): - return action_result.get_status() - - response = self._process_email_details( - action_result, - response, - email_addr, - endpoint, - extract_headers=param.get("extract_headers"), - download_attachments=param.get("download_attachments", False), - download_email=param.get("download_email", False), - ) - - action_result.add_data(response) - return action_result.set_status(phantom.APP_SUCCESS, "Successfully fetched email") - - def _handle_get_email_properties(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email_addr = param["email_address"] - message_id = param["id"] - endpoint = f"/users/{email_addr}" - - endpoint += f"/messages/{message_id}" - - select_list = [] - if param.get("get_headers"): - select_list.append("internetMessageHeaders") - if param.get("get_body"): - select_list.append("body") - if param.get("get_unique_body"): - select_list.append("uniqueBody") - if param.get("get_sender"): - select_list.append("sender") - if "properties_list" in param: - properties_list = param["properties_list"] - properties_list = [property.strip() for property in properties_list.strip().split(",") if property.strip()] - select_list += properties_list - - if select_list: - endpoint += "?$select={}".format(",".join(select_list)) - - ret_val, response = self._make_rest_call_helper(action_result, endpoint) - if phantom.is_fail(ret_val): - return action_result.get_status() - - if "internetMessageHeaders" in response: - response["internetMessageHeaders"] = self._flatten_headers(response["internetMessageHeaders"]) - - action_result.add_data(response) - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully fetched email") - - def _manage_data_duplication(self, emails, total_ingested, limit, max_emails): - """ - 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 - :return: limit: next cycle pagination limit, total_ingested: Total ingested emails till now - """ - total_ingested_current_cycle = limit - self._duplicate_count - total_ingested += total_ingested_current_cycle - - remaining_count = max_emails - total_ingested - if remaining_count <= 0: - return 0, total_ingested - - expected_duplicate_count_in_next_cycle = 0 - last_modified_time = emails[-1]["lastModifiedDateTime"] - - # Calculate the duplicate emails count we can get in the next cycle - for email in reversed(emails): - if email["lastModifiedDateTime"] != last_modified_time: - break - expected_duplicate_count_in_next_cycle += 1 - - limit = expected_duplicate_count_in_next_cycle + remaining_count - return limit, total_ingested - - def _handle_on_poll(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - config = self.get_config() - ingest_manner = config.get("ingest_manner", "oldest first") - - start_time = "" - if self.is_poll_now(): - max_emails = param[phantom.APP_JSON_CONTAINER_COUNT] - elif self._state.get("first_run", True): - # Integer validation for 'first_run_max_emails' config parameter - ret_val, max_emails = _validate_integer( - action_result, - config.get("first_run_max_emails", 1000), - "'Maximum Containers for scheduled polling first time' config", - ) - if phantom.is_fail(ret_val): - return action_result.get_status() - else: - # Integer validation for 'max_containers' config parameter - ret_val, max_emails = _validate_integer( - action_result, - config.get("max_containers", 100), - "'Maximum Containers for scheduled polling' config", - ) - if phantom.is_fail(ret_val): - return action_result.get_status() - - start_time = self._state["last_time"] - - if not config.get("email_address"): - return action_result.set_status(phantom.APP_ERROR, "Email Address to ingest must be supplied in asset!") - elif not config.get("folder"): - return action_result.set_status(phantom.APP_ERROR, "Folder to ingest from must be supplied in asset!") - - endpoint = "/users/{}".format(config.get("email_address")) - - if "folder" in config: - folder = config.get("folder", "") - if config.get("get_folder_id", True): - try: - dir_id, error, _ = self._get_folder_id(action_result, folder, config.get("email_address")) - except ReturnException as e: - self._dump_error_log(e) - - return action_result.get_status() - if dir_id: - folder = dir_id - else: - self.save_progress(error) - return action_result.set_status(phantom.APP_ERROR, error) - endpoint += f"/mailFolders/{folder}" - - endpoint += "/messages" - order = "asc" if ingest_manner == "oldest first" else "desc" - - params = {"$orderBy": f"lastModifiedDateTime {order}"} - - params["$select"] = ",".join(MSGOFFICE365_SELECT_PARAMETER_LIST) - - if start_time: - params["$filter"] = f"lastModifiedDateTime ge {start_time}" - - cur_limit = max_emails - total_ingested = 0 - - # If the ingestion manner is set for the latest emails, then the 0th index email is the latest - # in the list returned, else the last email is the latest. This will be used to store the - # last modified time in the state file - email_index = 0 if ingest_manner == "latest first" else -1 - - while True: - self._duplicate_count = 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() - - if not emails: - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - failed_email_ids = 0 - total_emails = len(emails) - - self.save_progress(f"Total emails fetched: {total_emails}") - if self.is_poll_now(): - self.save_progress("Ingesting all possible artifacts (ignoring maximum artifacts value) for POLL NOW") - - for index, email in enumerate(emails): - try: - self.send_progress("Processing email # {} with ID ending in: {}".format(index + 1, email["id"][-10:])) - ret_val = self._process_email_data(config, action_result, endpoint, email) - if phantom.is_fail(ret_val): - failed_email_ids += 1 - - self.debug_print("Error occurred while processing email ID: {}. {}".format(email.get("id"), action_result.get_message())) - except Exception as e: - failed_email_ids += 1 - 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: - return action_result.set_status( - phantom.APP_ERROR, - "Error occurred while processing all the email IDs", - ) - - 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 - self.save_state(deepcopy(self._state)) - - # Setting filter for next cycle - params["$filter"] = f"lastModifiedDateTime ge {last_time}" - - # 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) - if not cur_limit: - break - else: - break - else: - break - - # Update the 'first_run' value only if the ingestion gets successfully completed - if not self.is_poll_now() and self._state.get("first_run", True): - self._state["first_run"] = False - - return action_result.set_status(phantom.APP_SUCCESS) - - def _validate_range(self, email_range, action_result): - try: - mini, maxi = (int(x) for x in email_range.split("-")) - except Exception: - return action_result.set_status( - phantom.APP_ERROR, - "Unable to parse the range. Please specify the range as min_offset-max_offset", - ) - - if mini < 0 or maxi < 0: - return action_result.set_status( - phantom.APP_ERROR, - "Invalid min or max offset value specified in range", - ) - - if mini > maxi: - return action_result.set_status( - phantom.APP_ERROR, - "Invalid range value, min_offset greater than max_offset", - ) - - if maxi > MAX_END_OFFSET_VAL: - return action_result.set_status( - phantom.APP_ERROR, - f"Invalid range value. The max_offset value cannot be greater than {MAX_END_OFFSET_VAL}", - ) - - return phantom.APP_SUCCESS - - def _handle_generate_token(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - ret_val = self._get_token(action_result) - if phantom.is_fail(ret_val): - return action_result.get_status() - - self._state["admin_consent"] = True - - return action_result.set_status(phantom.APP_SUCCESS, "Token generated") - - def _handle_run_query(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - limit = param.get("limit") - # Integer validation for 'limit' action parameter - ret_val, limit = _validate_integer(action_result, limit, "'limit' action") - if phantom.is_fail(ret_val): - return action_result.get_status() - - # user - email_addr = param["email_address"] - endpoint = f"/users/{email_addr}" - query = "" - params = dict() - - if "internet_message_id" in param: - params = {"$filter": "internetMessageId eq '{}'".format(param["internet_message_id"])} - - elif "query" in param: - query = "?{}".format(param["query"]) - - else: - # search params - search_query = "" - if "subject" in param: - search_query += "subject:{} ".format(param["subject"]) - - if "body" in param: - search_query += "body:{} ".format(param["body"]) - - if "sender" in param: - search_query += "from:{} ".format(param["sender"]) - - if search_query: - params["$search"] = f'"{search_query[:-1]}"' - - folder_ids = [] - # searches through well known folders - if param.get("search_well_known_folders", False): - endpoint += "/mailFolders" - for folder in MSGOFFICE365_WELL_KNOWN_FOLDERS_FILTER: - folder_ids.append(folder) - - endpoint += "/{folder_id}" - - # folder - elif "folder" in param: - folder = param["folder"] - - if param.get("get_folder_id", True): - try: - dir_id, error, _ = self._get_folder_id(action_result, folder, email_addr) - except ReturnException as e: - self._dump_error_log(e) - - return action_result.get_status() - if dir_id: - folder = dir_id - else: - self.save_progress(error) - return action_result.set_status(phantom.APP_ERROR, error) - folder_ids.append(folder) - endpoint += "/mailFolders/{folder_id}" - - # that should be enough to create the endpoint - endpoint += "/messages" - - if folder_ids: - messages = [] - ret_val = False - for folder_id in folder_ids: - folder_ret_val, folder_messages = self._paginator( - action_result, - endpoint.format(folder_id=folder_id) + query, - limit, - params=params, - ) - - if phantom.is_fail(folder_ret_val): - continue - - ret_val = True - messages.extend(folder_messages) - - else: - ret_val, messages = self._paginator(action_result, endpoint, limit, params=params) - - if phantom.is_fail(ret_val): - msg = action_result.get_message() - if "$top" in msg or "$top/top" in msg: - msg += "The '$top' parameter is already used internally to handle pagination logic. " - msg += "If you want to restrict results in terms of number of output results, you can use the 'limit' parameter." - return action_result.set_status(phantom.APP_ERROR, msg) - return action_result.get_status() - - if not messages: - return action_result.set_status(phantom.APP_SUCCESS, MSGOFFICE365_NO_DATA_FOUND) - - action_result.update_data(messages) - action_result.update_summary({"emails_matched": action_result.get_data_size()}) - - return action_result.set_status(phantom.APP_SUCCESS) - - def _get_folder_id(self, action_result, folder, email): - # hindsight is always 20-20, set the folder path separator to be '/', thinking folder names allow '\' as a char. - # turns out even '/' is supported by office365, so let the action escape the '/' char if it's part of the folder name - folder_path = folder.replace("\\/", self._REPLACE_CONST) - folder_names = folder_path.split("/") - for i, folder_name in enumerate(folder_names): - folder_names[i] = folder_name.replace(self._REPLACE_CONST, "/").strip() - - # remove empty elements - path = list(filter(None, folder_names)) - - ret = list() - try: - dir_id = self._get_folder(action_result, path[0], email) - except ReturnException as e: - self._dump_error_log(e) - return None, f"Error occurred while fetching folder {path[0]}. {e}", None - - if not dir_id: - return None, f"Error: folder not found; {path[0]}", ret - - ret.append({"path": path[0], "folder": path[0], "folder_id": dir_id}) - - try: - for i, subf in enumerate(path[1:]): - subpath = "/".join(path[0 : i + 2]) - parent_id = dir_id - dir_id = self._get_child_folder(action_result, subf, parent_id, email) - - if not dir_id: - return ( - None, - f"Error: child folder not found; {subpath}", - ret, - ) - - ret.append({"path": subpath, "folder": subf, "folder_id": dir_id}) - except ReturnException as e: - self._dump_error_log(e) - return None, action_result.get_message(), None - - return dir_id, None, ret - - def _get_folder(self, action_result, folder, email): - params = {} - params["$filter"] = f"displayName eq '{folder}'" - endpoint = f"/users/{email}/mailFolders" - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, params=params) - - if phantom.is_fail(ret_val): - raise ReturnException(action_result.get_message()) - - value = response.get("value", []) - if len(value) > 0: - self._currentdir = value[0] - return value[0]["id"] - - return None - - def _get_child_folder(self, action_result, folder, parent_id, email): - params = {} - params["$filter"] = f"displayName eq '{folder}'" - endpoint = f"/users/{email}/mailFolders/{parent_id}/childFolders" - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, params=params) - - if phantom.is_fail(ret_val): - raise ReturnException() - - value = response.get("value", []) - if len(value) > 0: - self._currentdir = value[0] - return value[0]["id"] - - return None - - def _new_folder(self, action_result, folder, email): - data = json.dumps({"displayName": folder}) - endpoint = f"/users/{email}/mailFolders" - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, data=data, method="post") - if phantom.is_fail(ret_val): - raise ReturnException() - - if response.get("id", False): - self._currentdir = response - self.save_progress(f"Success({folder}): created folder in mailbox") - return response["id"] - - msg = f"Error({folder}): unable to create folder in mailbox" - self.save_progress(msg) - action_result.set_status(phantom.APP_ERROR, msg) - raise ReturnException() - - def _new_child_folder(self, action_result, folder, parent_id, email, pathsofar): - data = json.dumps({"displayName": folder}) - endpoint = f"/users/{email}/mailFolders/{parent_id}/childFolders" - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, data=data, method="post") - if phantom.is_fail(ret_val): - raise ReturnException() - - if response.get("id", False): - self._currentdir = response - self.save_progress(f"Success({folder}): created child folder in folder {pathsofar}") - return response["id"] - - msg = f"Error({folder}): unable to create child folder in folder {pathsofar}" - self.save_progress(msg) - action_result.set_status(phantom.APP_ERROR, msg) - raise ReturnException() - - def _handle_create_folder(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email = param["email_address"] - folder = param["folder"] - - minusp = param.get("all_subdirs", False) - - # hindsight is always 20-20, set the folder path separator to be '/', thinking folder names allow '\' as a char. - # turns out even '/' is supported by office365, so let the action escape the '/' char if it's part of the folder name - folder_path = folder.replace("\\/", self._REPLACE_CONST) - folder_names = folder_path.split("/") - for i, folder_name in enumerate(folder_names): - folder_names[i] = folder_name.replace(self._REPLACE_CONST, "/").strip() - - # remove empty elements - path = list(filter(None, folder_names)) - - if len(path) == 0: - msg = "Error: Invalid folder path" - self.save_progress(msg) - return action_result.set_status(phantom.APP_ERROR, msg) - - try: - dir_id = self._get_folder(action_result, path[0], email) - - # only one, create as "Folder" in mailbox - if len(path) == 1: - if dir_id: - msg = f"Error({path[0]}): folder already exists in mailbox" - self.save_progress(msg) - return action_result.set_status(phantom.APP_ERROR, msg) - - self._new_folder(action_result, path[0], email) - action_result.add_data(self._currentdir) - - # walk the path elements, creating each as needed - else: - pathsofar = "" - - # first deal with the initial Folder - if not dir_id: - if minusp: - dir_id = self._new_folder(action_result, path[0], email) - action_result.add_data(self._currentdir) - - else: - msg = f"Error({path[0]}): folder doesn't exists in mailbox" - self.save_progress(msg) - return action_result.set_status(phantom.APP_ERROR, msg) - - pathsofar += "/" + path[0] - parent_id = dir_id - - # next extract the final childFolder - final = path[-1] - path = path[1:-1] - - # next all the childFolders in between - for subf in path: - dir_id = self._get_child_folder(action_result, subf, parent_id, email) - - if not dir_id: - if minusp: - dir_id = self._new_child_folder(action_result, subf, parent_id, email, pathsofar) - action_result.add_data(self._currentdir) - - else: - msg = f"Error({subf}): child folder doesn't exists in folder {pathsofar}" - self.save_progress(msg) - return action_result.set_status(phantom.APP_ERROR, msg) - - pathsofar += "/" + subf - parent_id = dir_id - - # finally, the actual folder - dir_id = self._get_child_folder(action_result, final, parent_id, email) - if dir_id: - msg = f"Error: child folder {final} already exists in the folder {pathsofar}" - self.save_progress(msg) - return action_result.set_status(phantom.APP_ERROR, msg) - - dir_id = self._new_child_folder(action_result, final, parent_id, email, pathsofar) - action_result.add_data(self._currentdir) - - except ReturnException as e: - self._dump_error_log(e) - return action_result.get_status() - - action_result.update_summary( - { - "folders created": len(action_result.get_data()), - "folder": self._currentdir["id"], - } - ) - return action_result.set_status(phantom.APP_SUCCESS) - - def _handle_get_folder_id(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email = param["email_address"] - folder = param["folder"] - - try: - dir_id, error, ret = self._get_folder_id(action_result, folder, email) - - except ReturnException as e: - self._dump_error_log(e) - return action_result.get_status() - - if ret and len(ret) > 0: - for x in ret: - action_result.add_data(x) - - if dir_id: - action_result.update_summary({"folder_id": dir_id}) - return action_result.set_status(phantom.APP_SUCCESS) - - else: - self.save_progress(error) - return action_result.set_status(phantom.APP_ERROR, error) - - def _email_to_recipient(self, email: str): - recipient = {"emailAddress": {"address": email}} - return recipient - - def _create_draft_message( - self, - action_result, - subject: str, - body: str, - from_email: str, - *, - to_emails: list[str], - cc_emails: list[str], - bcc_emails: list[str], - headers: dict[str, str], - ): - endpoint = f"/users/{from_email}/messages" - req_headers = {"Prefer": 'IdType="ImmutableId"'} - msg = {"subject": subject, "body": {"contentType": "HTML", "content": body}} - if to_emails: - msg["toRecipients"] = [self._email_to_recipient(email) for email in to_emails] - if cc_emails: - msg["ccRecipients"] = [self._email_to_recipient(email) for email in cc_emails] - if bcc_emails: - msg["bccRecipients"] = [self._email_to_recipient(email) for email in bcc_emails] - if headers: - msg["internetMessageHeaders"] = [{"name": key, "value": value} for key, value in headers.items()] - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, method="post", headers=req_headers, data=json.dumps(msg)) - - if phantom.is_fail(ret_val): - return action_result.get_status(), None - - message_id = response["id"] - return action_result, message_id - - def _send_draft_message(self, action_result, user_id, message_id): - endpoint = f"/users/{user_id}/messages/{message_id}/send" - - ret_val, _ = self._make_rest_call_helper(action_result, endpoint, method="post") - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, f"Failed to send draft email with id: {message_id}"), None - - return action_result, message_id - - def _get_vault_info(self, vault_id): - _, _, vault_infos = phantom_vault.vault_info(container_id=self.get_container_id(), vault_id=vault_id) - if not vault_infos: - _, _, vault_infos = phantom_vault.vault_info(vault_id=vault_id) - return vault_infos[0] if vault_infos else None - - def _add_attachment_to_message(self, action_result, vault_id, user_id, message_id): - vault_info = self._get_vault_info(vault_id) - if not vault_info: - return action_result.set_status(phantom.APP_ERROR, f"Failed to find vault entry {vault_id}"), None - - if vault_info["size"] > MSGOFFICE365_UPLOAD_SESSION_CUTOFF: - ret_val, attachment_id = self._upload_large_attachment(action_result, vault_info, user_id, message_id) - else: - ret_val, attachment_id = self._upload_small_attachment(action_result, vault_info, user_id, message_id) - - return ret_val, attachment_id - - def _upload_small_attachment(self, action_result, vault_info, user_id, message_id): - endpoint = f"/users/{user_id}/messages/{message_id}/attachments" - with open(vault_info["path"], mode="rb") as file: - file_content = file.read() - data = { - "@odata.type": "#microsoft.graph.fileAttachment", - "name": vault_info["name"], - "contentType": vault_info["mime_type"], - "contentBytes": base64.b64encode(file_content).decode("ascii"), - "contentId": vault_info["vault_id"], - } - ret_val, response = self._make_rest_call_helper(action_result, endpoint, method="post", data=json.dumps(data)) - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, "Failed to upload vault entry {}".format(vault_info["vault_id"])), None - attachment_id = response["id"] - return phantom.APP_SUCCESS, attachment_id - - def _upload_large_attachment(self, action_result, vault_info, user_id, message_id): - endpoint = f"/users/{user_id}/messages/{message_id}/attachments/createUploadSession" - file_size = vault_info["size"] - data = { - "AttachmentItem": { - "attachmentType": "file", - "name": vault_info["name"], - "contentType": vault_info["mime_type"], - "contentId": vault_info["vault_id"], - "size": file_size, - } - } - ret_val, response = self._make_rest_call_helper(action_result, endpoint, method="post", data=json.dumps(data)) - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, "Failed to upload vault entry {}".format(vault_info["vault_id"])), None - upload_url = response["uploadUrl"] - - with open(vault_info["path"], mode="rb") as file: - for start_position in range(0, file_size, MSGOFFICE365_UPLOAD_LARGE_FILE_CUTOFF): - file_content = file.read(MSGOFFICE365_UPLOAD_LARGE_FILE_CUTOFF) - end_position = start_position + len(file_content) - 1 - headers = { - "Content-Type": "application/octet-stream", - "Content-Range": f"bytes {start_position}-{end_position}/{file_size}", - } - flag = True - while flag: - response = requests.put(upload_url, headers=headers, data=file_content) - - if response.status_code == 429 and response.headers["Retry-After"]: - retry_time = int(response.headers["Retry-After"]) - - if retry_time > 300: # throw error if wait time greater than 300 seconds - self.debug_print("Retry is canceled as retry time is greater than 300 seconds") - self._process_response(response, action_result) - return ( - action_result.set_status( - phantom.APP_ERROR, - f"Failed to upload file, {action_result.get_message()} Please retry after \ - {retry_time} seconds", - ), - None, - ) - self.debug_print(f"Retrying after {retry_time} seconds") - time.sleep(retry_time + 1) - elif not response.ok: - return ( - action_result.set_status( - phantom.APP_ERROR, - f"Failed to upload file, Error occurred : {response.status_code}, {response.text!s}", - ), - None, - ) - else: - flag = False - - result_location = response.headers.get("Location", "no_location_found") - match = re.search(r"Attachments\('(?P[^']+)'\)", result_location) - if not match: - return action_result.set_status(phantom.APP_ERROR, f"Unable to extract attachment id from url {result_location}"), None - attachment_id = match.group("attachment_id") - return phantom.APP_SUCCESS, attachment_id - - def _get_message(self, action_result, user_id, message_id): - endpoint = f"/users/{user_id}/messages/{message_id}" - - ret_val, response = self._make_rest_call_helper(action_result, endpoint, method="get") - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, f"Failed to get email with id: {message_id}"), None - - return action_result, response - - def _handle_send_email(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - config = self.get_config() - - from_email = param.get("from") or config.get("email_address") - to_emails = [email for x in param.get("to", "").split(",") if (email := x.strip())] - cc_emails = [email for x in param.get("cc", "").split(",") if (email := x.strip())] - bcc_emails = [email for x in param.get("bcc", "").split(",") if (email := x.strip())] - - subject = param["subject"] - - try: - headers = json.loads(param.get("headers", "{}")) - except Exception: - return action_result.set_status(phantom.APP_ERROR, "Please enter headers in a valid JSON format") - - body = param["body"] - vault_ids = [vault_id for x in param.get("attachments", "").split(",") if (vault_id := x.strip())] - - self.save_progress("Creating draft message") - ret_val, message_id = self._create_draft_message( - action_result, subject, body, from_email, headers=headers, to_emails=to_emails, cc_emails=cc_emails, bcc_emails=bcc_emails - ) - if phantom.is_fail(ret_val): - return action_result - self.save_progress(f"Created draft message with id: {message_id}") - - attachments = [] - for vault_id in vault_ids: - self.save_progress(f"Creating attachment for vault id: {vault_id}") - ret_val, attachment_id = self._add_attachment_to_message(action_result, vault_id, from_email, message_id) - if phantom.is_fail(ret_val): - return action_result - self.save_progress(f"Created attachment with id: {attachment_id}") - attachment = {"vault_id": vault_id, "attachment_id": attachment_id} - attachments.append(attachment) - - self.save_progress(f"Sending draft email with id: {message_id}") - ret_val, message_id = self._send_draft_message(action_result, from_email, message_id) - if phantom.is_fail(ret_val): - return action_result - self.save_progress("Successfully sent draft email.") - - self.save_progress(f"Getting sent email details with id: {message_id}") - ret_val, message_details = self._get_message(action_result, from_email, message_id) - if phantom.is_fail(ret_val): - return action_result - self.save_progress("Got sent email details.") - - action_result.add_data(message_details) - return action_result.set_status(phantom.APP_SUCCESS, "Successfully sent email") - - def _paginator(self, action_result, endpoint, limit=None, params=None, query=None, is_advance_query=False): - """ - This action is used to create an iterator that will paginate through responses from called methods. - - :param method_name: Name of method whose response is to be paginated - :param action_result: Object of ActionResult class - :param **kwargs: Dictionary of Input parameters - """ - - list_items = list() - next_link = None - headers = {} - - # maximum page size - page_size = MSGOFFICE365_PER_PAGE_COUNT - - if limit and limit < page_size: - page_size = limit - - if isinstance(params, dict): - params.update({"$top": page_size}) - else: - params = {"$top": page_size} - - if query: - params.update({"$filter": query}) - - if is_advance_query: - params["$count"] = "true" - headers["ConsistencyLevel"] = "eventual" - - while True: - ret_val, response = self._make_rest_call_helper(action_result, endpoint, nextLink=next_link, params=params, headers=headers) - - if phantom.is_fail(ret_val): - return action_result.get_status(), None - - if response.get("value"): - list_items.extend(response.get("value")) - - if limit and len(list_items) >= limit: - return phantom.APP_SUCCESS, list_items[:limit] - - next_link = response.get("@odata.nextLink") - if not next_link: - break - - params = None - - return phantom.APP_SUCCESS, list_items - - def _handle_update_email(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(param)) - - email_addr = param["email_address"] - message_id = param["id"] - - endpoint = f"/users/{email_addr}/messages/{message_id}" - - categories = param.get("categories") - subject = param.get("subject") - - if subject is None and categories is None: - return action_result.set_status(phantom.APP_ERROR, "Please specify one of the email properties to update") - - data_to_send = {} - if categories is not None: - categories = [x.strip() for x in categories.split(",")] - data_to_send["categories"] = categories - - if subject is not None: - data_to_send["subject"] = subject - - self.save_progress("Updating email") - ret_val, _ = self._make_rest_call_helper(action_result, endpoint, method="patch", data=json.dumps(data_to_send)) - if phantom.is_fail(ret_val): - return action_result.get_status() - - self.save_progress(f"Getting sent email details with id: {message_id}") - ret_val, message_details = self._get_message(action_result, email_addr, message_id) - if phantom.is_fail(ret_val): - return action_result - self.save_progress("Got sent email details.") - - action_result.add_data(message_details) - - return action_result.set_status(phantom.APP_SUCCESS, "Successfully updated email") - - def _report_message_sender(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - message = param["message_id"] - user = param["user_id"] - is_message_move_requested = param.get("is_message_move_requested", False) - report_action = param["report_action"] - - endpoint = f"/users/{user}/messages/{message}/reportMessage" - self.save_progress(f"endpoint {endpoint}") - - ret_val, response = self._make_rest_call_helper( - action_result, - endpoint, - data=json.dumps({"IsMessageMoveRequested": is_message_move_requested, "ReportAction": report_action}), - method="post", - beta=True, - ) - - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, f"Failed to report email with ID {message} as '{report_action}'") - - action_result.add_data(response) - return action_result.set_status(phantom.APP_SUCCESS) - - def _handle_block_sender(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - message = param["message_id"] - user = param["user_id"] - move_to_junk_folder = param.get("move_to_junk_folder", False) - - endpoint = f"/users/{user}/messages/{message}/markAsJunk" - self.save_progress(f"endpoint {endpoint}") - - ret_val, response = self._make_rest_call_helper( - action_result, endpoint, data=json.dumps({"moveToJunk": move_to_junk_folder}), method="post", beta=True - ) - - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, f"Moving email with id: {message} to junk folder failed") - - action_result.add_data(response) - return action_result.set_status(phantom.APP_SUCCESS) - - def _handle_unblock_sender(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - message = param["message_id"] - user = param["user_id"] - move_to_inbox = param.get("move_to_inbox", False) - - endpoint = f"/users/{user}/messages/{message}/markAsNotJunk" - self.save_progress(f"endpoint {endpoint}") - - ret_val, response = self._make_rest_call_helper( - action_result, endpoint, data=json.dumps({"moveToInbox": move_to_inbox}), method="post", beta=True - ) - - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, f"Moving email with id: {message} to inbox folder failed") - - action_result.add_data(response) - return action_result.set_status(phantom.APP_SUCCESS) - - def _handle_resolve_name(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - # id or userPrincipalName - email = param["email"] - - endpoint = f"/users?$filter=startswith(displayName,'{email}') or startswith(mail,'{email}')" - ret_val, responses = self._make_rest_call_helper(action_result, endpoint) - self.save_progress(f"Fetching user ended witch {ret_val}") - - if phantom.is_fail(ret_val): - return action_result.set_status(phantom.APP_ERROR, "Got invalid ret val") - - for response in responses.get("value"): - user_id = response.get("id") - - endpoint_other_mails = f"/users/{user_id}?$select=mailNickname,proxyAddresses,otherMails" - endpoint_other_address = f"/users/{user_id}?$select=city,state,street,postalCode" - endpoint_mailbox = f"/users/{user_id}/mailboxSettings/userPurpose" - - ret_val_proxy, response_proxy = self._make_rest_call_helper(action_result, endpoint_other_mails) - ret_val_address, response_address = self._make_rest_call_helper(action_result, endpoint_other_address) - ret_val_mailbox, response_mailbox = self._make_rest_call_helper(action_result, endpoint_mailbox) - - self.save_progress(f"Got statuses: mails: {ret_val_proxy}, address: {ret_val_address}, mailbox: {ret_val_mailbox}") - - action_result.add_data(response | response_proxy | response_address | (response_mailbox or {"userPurpose": None})) - - return action_result.set_status(phantom.APP_SUCCESS) - - def _handle_get_mailbox_messages(self, param): - self.save_progress(f"In action handler for: {self.get_action_identifier()}") - action_result = self.add_action_result(ActionResult(dict(param))) - - email_address = param["email_address"] - folder = param.get("folder", MSGOFFICE365_DEFAULT_FOLDER) - limit = param.get("limit", MSGOFFICE365_DEFAULT_LIMIT) - offset = param.get("offset", 0) - ingest = param.get("plus_ingest", False) - download_attachments = param.get("download_attachments", False) - download_email = param.get("download_email", False) - extract_headers = param.get("extract_headers", False) - - ret_val, limit = _validate_integer(action_result, limit, "'limit' action") - if phantom.is_fail(ret_val): - return action_result.get_status() - - # Limit should not exceed 100 per request for timeout reasons - if limit > 100: - return action_result.set_status(phantom.APP_ERROR, "Limit should not exceed 100 messages per request") - - ret_val, offset = _validate_integer(action_result, offset, "'offset' action", allow_zero=True) - if phantom.is_fail(ret_val): - return action_result.get_status() - - endpoint = f"/users/{email_address}/mailFolders/{folder}/messages" - params = { - "$top": limit, - "$orderby": MSGOFFICE365_ORDERBY_RECEIVED_DESC, - "$select": ",".join(MSGOFFICE365_SELECT_PARAMETER_LIST), - "$skip": offset, - } - - date_filters = [] - if param.get("start_date"): - date_filters.append(MSGOFFICE365_RECEIVED_DATE_FILTER.format(operator="ge", date=param.get("start_date"))) - if param.get("end_date"): - date_filters.append(MSGOFFICE365_RECEIVED_DATE_FILTER.format(operator="le", date=param.get("end_date"))) - - if date_filters: - params["$filter"] = MSGOFFICE365_DATE_FILTER_AND.join(date_filters) - - ret_val, messages = self._paginator(action_result, endpoint, limit=limit, params=params) - - if phantom.is_fail(ret_val): - return action_result.get_status() - - failed_email_ids = 0 - duplicate_count = self._duplicate_count - total_emails = len(messages) - - for index, email in enumerate(messages): - try: - # Perform additional processing of attachments/data for email - message_endpoint = f"/users/{email_address}/messages/{email['id']}" - email = self._process_email_details( - action_result, - email, - email_address, - message_endpoint, - extract_headers=extract_headers, - download_attachments=download_attachments, - download_email=download_email, - ) - - action_result.add_data(email) - - if ingest: - try: - # Ingest email data - ret_val = self._process_email_data(self.get_config(), action_result, endpoint, email) - if phantom.is_fail(ret_val): - failed_email_ids += 1 - continue - - except Exception as e: - failed_email_ids += 1 - self.debug_print(f"Exception occurred while processing email ID: {email.get('id')}. Error: {e!s}") - - except Exception as e: - failed_email_ids += 1 - self.debug_print(f"Exception occurred while processing email ID: {email.get('id')}. Error: {e!s}") - - if failed_email_ids == total_emails and total_emails > 0: - return action_result.set_status(phantom.APP_ERROR, f"Error occurred while processing all {total_emails} email IDs.") - - summary = action_result.update_summary({}) - summary["total_messages"] = total_emails - if ingest: - duplicate_count = self._duplicate_count - duplicate_count - summary["new_emails_ingested"] = total_emails - failed_email_ids - duplicate_count - summary["duplicate_emails"] = duplicate_count - summary["failed_emails"] = failed_email_ids - - status_msg = f"Successfully retrieved {total_emails} messages from {email_address}'s {folder} folder (offset: {offset})" - if ingest: - status_msg += f" and ingested {total_emails - failed_email_ids - duplicate_count} new messages" - if duplicate_count: - status_msg += f" ({duplicate_count} duplicates skipped)" - if failed_email_ids: - status_msg += f" ({failed_email_ids} failed)" - - return action_result.set_status(phantom.APP_SUCCESS, status_msg) - - def handle_action(self, param): - ret_val = phantom.APP_SUCCESS - - # Get the action that we are supposed to execute for this App Run - action_id = self.get_action_identifier() - - self.debug_print("action_id", self.get_action_identifier()) - - if action_id == "resolve_name": - ret_val = self._handle_resolve_name(param) - - if action_id == "report_message": - ret_val = self._report_message_sender(param) - - if action_id == "block_sender": - ret_val = self._handle_block_sender(param) - - if action_id == "unblock_sender": - ret_val = self._handle_unblock_sender(param) - - if action_id == "test_connectivity": - ret_val = self._handle_test_connectivity(param) - - elif action_id == "copy_email": - ret_val = self._handle_copy_email(param) - - elif action_id == "move_email": - ret_val = self._handle_move_email(param) - - elif action_id == "delete_email": - ret_val = self._handle_delete_email(param) - - elif action_id == "delete_event": - ret_val = self._handle_delete_event(param) - - elif action_id == "get_email": - ret_val = self._handle_get_email(param) - - elif action_id == "get_email_properties": - ret_val = self._handle_get_email_properties(param) - - elif action_id == "on_poll": - ret_val = self._handle_on_poll(param) - - elif action_id == "run_query": - ret_val = self._handle_run_query(param) - - elif action_id == "list_events": - ret_val = self._handle_list_events(param) - - elif action_id == "list_groups": - ret_val = self._handle_list_groups(param) - - elif action_id == "list_group_members": - ret_val = self._handle_list_group_members(param) - - elif action_id == "list_users": - ret_val = self._handle_list_users(param) - - elif action_id == "list_folders": - ret_val = self._handle_list_folders(param) - - elif action_id == "oof_check": - ret_val = self._handle_oof_check(param) - - elif action_id == "generate_token": - ret_val = self._handle_generate_token(param) - - elif action_id == "create_folder": - ret_val = self._handle_create_folder(param) - - elif action_id == "get_folder_id": - ret_val = self._handle_get_folder_id(param) - - elif action_id == "list_rules": - ret_val = self._handle_list_rules(param) - - elif action_id == "get_rule": - ret_val = self._handle_get_rule(param) - - elif action_id == "send_email": - ret_val = self._handle_send_email(param) - - elif action_id == "update_email": - ret_val = self._handle_update_email(param) - - elif action_id == "get_mailbox_messages": - ret_val = self._handle_get_mailbox_messages(param) - - return ret_val - - def _get_private_key(self, action_result): - # When the private key is copied/pasted to an asset parameter - # SOAR converts \n to spaces. This code fixes that and rebuilds - # the private key as it should be - - if self._certificate_private_key is not None: - p = re.compile("(-----.*?-----) (.*) (-----.*?-----)") - m = p.match(self._certificate_private_key) - - if m: - private_key = "\n".join([m.group(1), m.group(2).replace(" ", "\n"), m.group(3)]) - return phantom.APP_SUCCESS, private_key - else: - return action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_CBA_KEY_ERROR), None - - def _generate_new_cba_access_token(self, action_result): - self.save_progress("Generating token using Certificate Based Authentication...") - - # reset the state - self._state.pop("admin_auth", None) - self._state.pop("non_admin_auth", None) - - # Certificate Based Authentication requires both Certificate Thumbprint and Certificate Private Key - if not (self._thumbprint and self._certificate_private_key): - self.save_progress(MSGOFFICE365_CBA_AUTH_ERROR) - return self.set_status(phantom.APP_ERROR), None - - # Check non-interactive is enabled for CBA auth - if not self._admin_consent: - self.save_progress(MSGOFFICE365_CBA_ADMIN_CONSENT_ERROR) - return self.set_status(phantom.APP_ERROR), None - - ret_val, self._private_key = self._get_private_key(action_result) - - if phantom.is_fail(ret_val): - return action_result.get_status(), None - - try: - app = msal.ConfidentialClientApplication( - self._client_id, - authority=MSGOFFICE365_AUTHORITY_URL.format(tenant=self._tenant), - client_credential={"thumbprint": self._thumbprint, "private_key": self._private_key}, - ) - except Exception as e: - error_msg = _get_error_msg_from_exception(e, self) - return ( - action_result.set_status( - phantom.APP_ERROR, - f"Please check your configured parameters. Error while using certificate to authenticate. {error_msg}", - ), - None, - ) - - self.debug_print("Requesting new token from Azure AD.") - res_json = app.acquire_token_for_client(scopes=[MSGOFFICE365_DEFAULT_SCOPE]) - - if error := res_json.get("error"): - # replace thumbprint to dummy value - error_message = f"{error}: {res_json.get('error_description')}".replace(self._thumbprint[4:], "xxxxxxxxxxxxxxxxxxx") - return action_result.set_status(phantom.APP_ERROR, error_message), None - - return phantom.APP_SUCCESS, res_json - - def _generate_new_oauth_access_token(self, action_result): - self.save_progress("Generating token using OAuth Authentication...") - req_url = SERVER_TOKEN_URL.format(self._tenant) - headers = {"Content-Type": "application/x-www-form-urlencoded"} - - data = { - "client_id": self._client_id, - "client_secret": self._client_secret, - "grant_type": "client_credentials", - } - - if not self._admin_access: - data["scope"] = "offline_access " + self._scope - else: - data["scope"] = MSGOFFICE365_DEFAULT_SCOPE - - if not self._admin_access: - if self._state.get("code"): - self.save_progress("Generating token using authorization code") - data["redirect_uri"] = self._state.get("redirect_uri") - data["code"] = self._state.get("code") - data["grant_type"] = "authorization_code" - self._state.pop("code") - elif self._refresh_token: - self.save_progress("Generating token using refresh token") - data["refresh_token"] = self._refresh_token - data["grant_type"] = "refresh_token" - else: - return action_result.set_status( - phantom.APP_ERROR, - "Unexpected details retrieved from the state file.", - ) - - self.debug_print("Generating token...") - return self._make_rest_call(action_result, req_url, headers=headers, data=data, method="post") - - def _get_token(self, action_result): - # Determine the authentication type and function to generate the access token - # Automatic auth - If client Secret exists, it will take priority and follow the OAuth workflow. - auth_type, generate_token_func = ( - ("cba", self._generate_new_cba_access_token) - if self._auth_type == "cba" or not self._client_secret - else ("oauth", self._generate_new_oauth_access_token) - ) - - # Attempt to generate the access token and check for failure - ret_val, resp_json = generate_token_func(action_result) - if phantom.is_fail(ret_val): - return action_result.get_status() - - # Save the determined auth type - self._state["auth_type"] = auth_type - - if auth_type == "cba" and self._admin_consent: - self._state["admin_consent"] = True - - # Save the response on the basis of admin_access - if self._admin_access: - # if admin consent already provided, save to state - if self._admin_consent: - self._state["admin_consent"] = True - self._state["admin_auth"] = resp_json - else: - self._state["non_admin_auth"] = resp_json - - # Fetching the access token and refresh token - self._access_token = resp_json.get("access_token") - self._refresh_token = resp_json.get("refresh_token") - - # Save state - self.save_state(self._state) - self._state = self.load_state() - - if not isinstance(self._state, dict): - self.debug_print(MSGOFFICE365_STATE_FILE_CORRUPT_ERROR) - self._reset_state_file() - - return action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_STATE_FILE_CORRUPT_ERROR) - - # Scenario - - # - # If the corresponding state file doesn't have correct owner, owner group or permissions, - # the newly generated token is not being saved to state file and automatic workflow for token has been stopped. - # So we have to check that token from response and token which are saved to state file - # after successful generation of new token are same or not. - - if self._admin_access: - if self._access_token != self._state.get("admin_auth", {}).get("access_token"): - return action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_INVALID_PERMISSION_ERROR) - else: - if self._access_token != self._state.get("non_admin_auth", {}).get("access_token"): - return action_result.set_status(phantom.APP_ERROR, MSGOFFICE365_INVALID_PERMISSION_ERROR) - - self.debug_print("Token generated successfully") - return action_result.set_status(phantom.APP_SUCCESS) - - def _get_consent(self, action_result): - self.save_progress("Getting App REST endpoint URL") - - # Get the URL to the app's REST Endpoint, this is the url that the TC dialog - # box will ask the user to connect to - ret_val, app_rest_url = self._get_url_to_app_rest(action_result) - app_state = {} - if phantom.is_fail(ret_val): - self.save_progress(f"Unable to get the URL to the app's REST Endpoint. Error: {action_result.get_message()}") - return action_result.set_status(phantom.APP_ERROR) - - # create the url that the oauth server should re-direct to after the auth is completed - # (success and failure), this is added to the state so that the request handler will access - # it later on - redirect_uri = f"{app_rest_url}/result" - app_state["redirect_uri"] = redirect_uri - - self.save_progress("Using OAuth Redirect URL as:") - self.save_progress(redirect_uri) - - if self._admin_access: - # Create the url for fetching administrator consent - admin_consent_url = f"https://login.microsoftonline.com/{self._tenant}/adminconsent" - admin_consent_url += f"?client_id={self._client_id}" - admin_consent_url += f"&redirect_uri={redirect_uri}" - admin_consent_url += f"&state={self._asset_id}" - else: - # Scope is required for non-admin access - if not self._scope: - self.save_progress(MSGOFFICE365_NON_ADMIN_SCOPE_ERROR) - return action_result.set_status(phantom.APP_ERROR) - # Create the url authorization, this is the one pointing to the oauth server side - admin_consent_url = f"https://login.microsoftonline.com/{self._tenant}/oauth2/v2.0/authorize" - admin_consent_url += f"?client_id={self._client_id}" - admin_consent_url += f"&redirect_uri={redirect_uri}" - admin_consent_url += f"&state={self._asset_id}" - admin_consent_url += f"&scope={self._scope}" - admin_consent_url += "&response_type=code" - - app_state["admin_consent_url"] = admin_consent_url - - # The URL that the user should open in a different tab. - # This is pointing to a REST endpoint that points to the app - url_to_show = f"{app_rest_url}/start_oauth?asset_id={self._asset_id}&" - - # Save the state, will be used by the request handler - _save_app_state(app_state, self._asset_id, self) - - self.save_progress("Please connect to the following URL from a different tab to continue the connectivity process") - self.save_progress(url_to_show) - self.save_progress(MSGOFFICE365_AUTHORIZE_TROUBLESHOOT_MSG) - - time.sleep(5) - - completed = False - - app_dir = os.path.dirname(os.path.abspath(__file__)) - auth_status_file_path = f"{app_dir}/{self._asset_id}_{TC_FILE}" - - if self._admin_access: - self.save_progress("Waiting for Admin Consent to complete") - else: - self.save_progress("Waiting for Authorization to complete") - - for i in range(0, 40): - self.send_progress("{}".format("." * (i % 10))) - - if os.path.isfile(auth_status_file_path): - completed = True - os.unlink(auth_status_file_path) - break - - time.sleep(TC_STATUS_SLEEP) - - if not completed: - self.save_progress("Authentication process does not seem to be completed. Timing out") - return action_result.set_status(phantom.APP_ERROR) - - self.send_progress("") - - # Load the state again, since the http request handlers would have saved the result of the admin consent or authorization - self._state = _load_app_state(self._asset_id, self) - - if not self._state: - self.save_progress("Authorization not received or not given") - self.save_progress("Test Connectivity Failed") - return action_result.set_status(phantom.APP_ERROR) - else: - if self._admin_access: - if not self._state.get("admin_consent"): - self.save_progress("Admin Consent not received or not given") - self.save_progress("Test Connectivity Failed") - return action_result.set_status(phantom.APP_ERROR) - else: - if not self._state.get("code"): - self.save_progress("Authorization code not received or not given") - self.save_progress("Test Connectivity Failed") - return action_result.set_status(phantom.APP_ERROR) - - # Deleting the local state file because of it replicates with actual state file while installing the app - current_file_path = pathlib.Path(__file__).resolve() - input_file = f"{self._asset_id}_state.json" - state_file_path = current_file_path.with_name(input_file) - state_file_path.unlink() - - return action_result.set_status(phantom.APP_SUCCESS) - - def _reset_state_file(self): - """ - This method resets the state file. - """ - self.debug_print("Resetting the state file with the default format") - self._state = {"app_version": self.get_app_json().get("app_version")} - - def initialize(self): - action_id = self.get_action_identifier() - action_result = ActionResult() - - self._currentdir = None - - # Load the state in initialize - config = self.get_config() - self._asset_id = self.get_asset_id() - - self._tenant = config["tenant"] - self._client_id = config["client_id"] - self._auth_type = MSGOFFICE365_AUTH_TYPES.get(config.get("auth_type", MSGOFFICE365_AUTH_AUTOMATIC)) - self._client_secret = config.get("client_secret") - self._admin_access = config.get("admin_access") - self._admin_consent = config.get("admin_consent") - self._thumbprint = config.get("certificate_thumbprint") - self._certificate_private_key = config.get("certificate_private_key") - self._scope = config.get("scope") if config.get("scope") else None - - if self._auth_type == "cba": - # Certificate Based Authentication requires both Certificate Thumbprint and Certificate Private Key - if not (self._thumbprint and self._certificate_private_key): - return self.set_status(phantom.APP_ERROR, MSGOFFICE365_CBA_AUTH_ERROR) - - # Check non-interactive is enabled for CBA auth - if not self._admin_consent: - return self.set_status(phantom.APP_ERROR, MSGOFFICE365_CBA_ADMIN_CONSENT_ERROR) - elif self._auth_type == "oauth": - # OAuth Authentication requires Client Secret - if not self._client_secret: - return self.set_status(phantom.APP_ERROR, MSGOFFICE365_OAUTH_AUTH_ERROR) - else: - # Must either supply cba or oauth credentials for automatic auth - if not self._client_secret and not (self._thumbprint and self._certificate_private_key): - return self.set_status(phantom.APP_ERROR, MSGOFFICE365_AUTOMATIC_AUTH_ERROR) - - self._number_of_retries = config.get("retry_count", MSGOFFICE365_DEFAULT_NUMBER_OF_RETRIES) - ret_val, self._number_of_retries = _validate_integer( - self, self._number_of_retries, "'Maximum attempts to retry the API call' asset configuration" - ) - - if phantom.is_fail(ret_val): - return self.get_status() - - self._retry_wait_time = config.get("retry_wait_time", MSGOFFICE365_DEFAULT_RETRY_WAIT_TIME) - ret_val, self._retry_wait_time = _validate_integer( - self, - self._retry_wait_time, - "'Delay in seconds between retries' asset configuration", - ) - if phantom.is_fail(ret_val): - return self.get_status() - - # Load all the asset configuration in global variables - self._state = self.load_state() - - if not self._admin_access: - if not self._scope and self._auth_type == "oauth": - return self.set_status(phantom.APP_ERROR, MSGOFFICE365_NON_ADMIN_SCOPE_ERROR) - - self._access_token = self._state.get("non_admin_auth", {}).get("access_token", None) - self._refresh_token = self._state.get("non_admin_auth", {}).get("refresh_token", None) - else: - self._access_token = self._state.get("admin_auth", {}).get("access_token", None) - - if action_id == "test_connectivity": - # User is trying to complete the authentication flow, so just return True from here so that test connectivity continues - return phantom.APP_SUCCESS - - admin_consent = self._state.get("admin_consent") - - # if it was not and the current action is not test connectivity then it's an error - if self._admin_access and not admin_consent: - return self.set_status(phantom.APP_ERROR, MSGOFFICE365_RUN_CONNECTIVITY_MSG) - - if not self._admin_access and not self._access_token: - ret_val = self._get_token(action_result) - - if phantom.is_fail(ret_val): - return self.set_status( - phantom.APP_ERROR, - f"{MSGOFFICE365_RUN_CONNECTIVITY_MSG}. {action_result.get_message()}", - ) - - # Create ProcessEmail Object for on_poll - self._process_email = ProcessEmail(self, config) - - return phantom.APP_SUCCESS - - def _get_fips_enabled(self): - try: - from phantom_common.install_info import is_fips_enabled - except ImportError: - return False - - fips_enabled = is_fips_enabled() - if fips_enabled: - self.debug_print("FIPS is enabled") - else: - self.debug_print("FIPS is not enabled") - return fips_enabled - - def finalize(self): - # Save the state, this data is saved across actions and app upgrades - self.save_state(self._state) - return phantom.APP_SUCCESS - - -if __name__ == "__main__": - import argparse - - import pudb - - pudb.set_trace() - - argparser = argparse.ArgumentParser() - - argparser.add_argument("input_test_json", help="Input Test JSON file") - argparser.add_argument("-u", "--username", help="username", required=False) - argparser.add_argument("-p", "--password", help="password", required=False) - argparser.add_argument( - "-v", - "--verify", - action="store_true", - help="verify", - required=False, - default=False, - ) - - args = argparser.parse_args() - session_id = None - verify = args.verify - - if args.username and args.password: - login_url = f"{BaseConnector._get_phantom_base_url()}login" - try: - print("Accessing the Login page") - r = requests.get(login_url, verify=verify, timeout=MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT) - csrftoken = r.cookies["csrftoken"] - data = { - "username": args.username, - "password": args.password, - "csrfmiddlewaretoken": csrftoken, - } - headers = { - "Cookie": f"csrftoken={csrftoken}", - "Referer": login_url, - } - - print("Logging into Platform to get the session id") - r2 = requests.post( - login_url, - verify=verify, - data=data, - headers=headers, - timeout=MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT, - ) - session_id = r2.cookies["sessionid"] - - except Exception as e: - print(f"Unable to get session id from the platform. Error: {e!s}") - sys.exit(1) - - if len(sys.argv) < 2: - print("No test json specified as input") - sys.exit(0) - - with open(sys.argv[1]) as f: - in_json = f.read() - in_json = json.loads(in_json) - print(json.dumps(in_json, indent=4)) - - connector = Office365Connector() - connector.print_progress_message = True - - if session_id is not None: - in_json["user_session_token"] = session_id - - ret_val = connector._handle_action(json.dumps(in_json), None) - print(json.dumps(json.loads(ret_val), indent=4)) - - sys.exit(0) diff --git a/office365_get_email.html b/office365_get_email.html deleted file mode 100644 index ba9f3ba..0000000 --- a/office365_get_email.html +++ /dev/null @@ -1,342 +0,0 @@ -{% extends 'widgets/widget_template.html' %} -{% load custom_template %} -{% block custom_title_prop %} - {% if title_logo %} - style="background-size: auto 60%; background-position: 50%; background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');" - {% endif %} -{% endblock %} -{% block title1 %}{{ title1 }}{% endblock %} -{% block title2 %}{{ title2 }}{% endblock %} -{% block custom_tools %}{% endblock %} -{% block widget_content %} - - - -
- - {% for result in results %} - - {% if not result.data %} -

No data found

- {% else %} -
-

Action Parameters

- - - - - - - - - - {% if result.param.download_attachments %} - - - - - {% endif %} - {% if result.param.extract_headers %} - - - - - {% endif %} -
Message ID - - {{ result.param.id }} -   - - -
Email Address - - {{ result.param.email_address }} -   - - -
Download attachments to vault{{ result.param.download_attachments }}
Extract email headers{{ result.param.extract_headers }}
-
-
-

Data

- - - - - - - - - - - - {% for data in result.data %} - - - - - {% if data.from is not None %} - - {% else %} - - {% endif %} - {% if data.toRecipients %} - - {% else %} - - {% endif %} - - - {% if data.event.id %} - - {% else %} - - {% endif %} - - {% endfor %} -
Message IDSubjectBody PerviewFromToHas Attachments?Internet Message IDEvent ID
- - {{ data.id }} -   - - - - - {{ data.subject }} -   - - - {{ data.bodyPreview }} - - {{ data.from.emailAddress.address }} -   - - - None - - {% for to in data.toRecipients %} - - - - {% endfor %} -
- - {{ to.emailAddress.address }} -   - - -
-
None{{ data.hasAttachments }} - - {{ data.internetMessageId }} -   - - - - - {{ data.event.id }} -   - - - None
-
-
- {% with result.data|first as email_data %} - {% if email_data.hasAttachments %} -

Attachments

- {% if email_data.attachment_data.file_attachment %} - - - - - - - - - {% for file in email_data.attachment_data.file_attachment %} - - - - - - - {% endfor %} - -
Attachment NameVault IDAttachment TypeContent Type
{{ file.name }} - - {{ file.vaultId }} -   - - - {{ file.attachmentType }}{{ file.contentType }}
- {% endif %} - {% if email_data.attachment_data.item_attachment %} - - - - - - - - - {% for item in email_data.attachment_data.item_attachment %} - - - - - - - - {% endfor %} -
Attachment NameVault IDAttachment TypeItem Attachment TypeContent Type
{{ item.name }} - - {{ item.vaultId }} -   - - - {{ item.attachmentType }}{{ item.itemType }}{{ item.contentType }}
- {% endif %} - {% if email_data.attachment_data.reference_attachment %} - - - - - - - {% for reference in email_data.attachment_data.reference_attachment %} - - - - - - {% endfor %} -
Attachment NameAttachment TypeContent Type
{{ reference.name }}{{ reference.attachmentType }}{{ reference.contentType }}
- {% endif %} - {% if email_data.attachment_data.other_attachment %} - - - - - - - {% for attachment in email_data.attachment_data.other_attachment %} - - - - - - {% endfor %} -
Attachment NameAttachment TypeContent Type
{{ attachment.name }}{{ attachment.attachmentType }}{{ attachment.contentType }}
- {% endif %} - {% endif %} - {% endwith %} -
- {% endif %} - {% endfor %} -
- -{% endblock %} diff --git a/office365_get_rule.html b/office365_get_rule.html deleted file mode 100644 index f8334e1..0000000 --- a/office365_get_rule.html +++ /dev/null @@ -1,181 +0,0 @@ -{% extends 'widgets/widget_template.html' %} -{% load custom_template %} -{% block custom_title_prop %} - {% if title_logo %} - style="background-size: auto 60%; background-position: 50%; background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');" - {% endif %} -{% endblock %} -{% block title1 %}{{ title1 }}{% endblock %} -{% block title2 %}{{ title2 }}{% endblock %} -{% block custom_tools %}{% endblock %} -{% block widget_content %} - - - -
- - {% for result in results %} - {% if not result.data %} -

No data found

- {% else %} -
-

Action Parameters

- - - - - - - - - -
User ID - - {{ result.param.user_id }} -   - - -
Rule ID - - {{ result.param.rule_id }} -   - - -
-
-
-

Data

- - - - - - - {% for data in result.data %} - - - - {% endfor %} -
RULE DISPLAY NAMERULE ACTIONSRULE CONDITIONS
{{ data.displayName }} - - {% for key, value in data.items %} - {% if 'action' in key %} - - - - - {% endif %} - {% endfor %} - -
{{ key }}{{ value }}
-
- - {% for key, value in data.items %} - {% if 'condition' in key %} - - - - - {% endif %} - {% endfor %} - -
{{ key }}{{ value }}
-
-
- {% endif %} - {% endfor %} -
- -{% endblock %} diff --git a/office365_list_events.html b/office365_list_events.html deleted file mode 100644 index e191a66..0000000 --- a/office365_list_events.html +++ /dev/null @@ -1,186 +0,0 @@ -{% extends 'widgets/widget_template.html' %} -{% load custom_template %} -{% block custom_title_prop %} - {% if title_logo %} - style="background-size: auto 60%; background-position: 50%; background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');" - {% endif %} -{% endblock %} -{% block title1 %}{{ title1 }}{% endblock %} -{% block title2 %}{{ title2 }}{% endblock %} -{% block custom_tools %}{% endblock %} -{% block widget_content %} - - - -
- - {% for result in results %} - - {% if not result.data %} -

No data found

- {% else %} -
-

Info

- - {% if result.param.user_id %} - - - - - {% endif %} - {% if result.param.group_id %} - - - - - {% endif %} - {% if result.param.limit %} - - - - - {% endif %} - {% if result.param.filter %} - - - - - {% endif %} -
User ID - - {{ result.param.user_id }} -   - - -
Group ID - - {{ result.param.group_id }} -   - - -
Limit{{ result.param.limit }}
Filter{{ result.param.filter }}
-
-
-

Data

- - - - - - - - - {% for data in result.data %} - - {% if data.id %} - - {% else %} - - {% endif %} - - - - - - {% endfor %} -
Event IDSubjectStart TimeEnd TimeAttendee(s)
- - {{ data.id }} -   - - - None{{ data.subject }}{{ data.start.dateTime }}{{ data.end.dateTime }}{{ data.attendee_list }}
-
- {% endif %} - {% endfor %} -
- -{% endblock %} diff --git a/office365_list_rules.html b/office365_list_rules.html deleted file mode 100644 index 65c7fb6..0000000 --- a/office365_list_rules.html +++ /dev/null @@ -1,141 +0,0 @@ -{% extends 'widgets/widget_template.html' %} -{% load custom_template %} -{% block custom_title_prop %} - {% if title_logo %} - style="background-size: auto 60%; background-position: 50%; background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');" - {% endif %} -{% endblock %} -{% block title1 %}{{ title1 }}{% endblock %} -{% block title2 %}{{ title2 }}{% endblock %} -{% block custom_tools %}{% endblock %} -{% block widget_content %} - - - -
- - {% for result in results %} - - {% if not result.data %} -

No data found

- {% else %} -
-

Rules List

- - - - - - - - {% for data in result.data %} - - - - {% if data.actions.delete %} - - {% else %} - - {% endif %} - - - {% endfor %} -
RULE IDRULE NAMEDELETE EMAIL ENABLEDACTION
- - {{ data.id }} -   - - - {{ data.displayName }}{{ data.actions.delete }}None{{ data.actions }}
-
- {% endif %} - {% endfor %} -
- -{% endblock %} diff --git a/office365_resolve_name.html b/office365_resolve_name.html deleted file mode 100644 index 353e101..0000000 --- a/office365_resolve_name.html +++ /dev/null @@ -1,381 +0,0 @@ -{% extends 'widgets/widget_template.html' %} -{% load custom_template %} -{% block custom_title_prop %} - {% if title_logo %} - style="background-size: auto 60%; background-position: 50%; background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');" - {% endif %} -{% endblock %} -{% block title1 %}{{ title1 }}{% endblock %} -{% block title2 %}{{ title2 }}{% endblock %} -{% block custom_tools %}{% endblock %} -{% block widget_content %} - - - -
- - {% for result in results %} - - {% if not result.data %} -

No data found

- {% else %} -
-

Action Parameters

- - - - - -
Email Address - - {{ result.param.email }} -   - - -
-
-
-

Data

- - - - - - - - - - - - - - - - - - - - - {% for data in result.data %} - - {% if data.id is not None %} - - {% else %} - - {% endif %} - {% if data.userPrincipalName is not None %} - - {% else %} - - {% endif %} - {% if data.givenName is not None %} - - {% else %} - - {% endif %} - {% if data.surname is not None %} - - {% else %} - - {% endif %} - {% if data.displayName is not None %} - - {% else %} - - {% endif %} - {% if data.mailNickname is not None %} - - {% else %} - - {% endif %} - {% if data.mail is not None %} - - {% else %} - - {% endif %} - {% if data.otherMails is not None %} - - {% else %} - - {% endif %} - {% if data.proxyAddresses is not None %} - - {% else %} - - {% endif %} - {% if data.jobTitle is not None %} - - {% else %} - - {% endif %} - {% if data.officeLocation is not None %} - - {% else %} - - {% endif %} - {% if data.value is not None %} - - {% else %} - - {% endif %} - {% if data.mobilePhone is not None %} - - {% else %} - - {% endif %} - {% if data.businessPhones is not None %} - - {% else %} - - {% endif %} - {% if data.preferredLanguage is not None %} - - {% else %} - - {% endif %} - {% if data.state is not None %} - - {% else %} - - {% endif %} - {% if data.postalCode is not None %} - - {% else %} - - {% endif %} - - {% endfor %} -
idPrincipal namegiven namesurnamedisplay namemail nicknamemailother mailsproxy email addressesjob titleoffice locationvaluemobile phonebusiness phonespreferred languagestatepostal code
- - {{ data.id }} -   - - - None - - {{ data.userPrincipalName }} -   - - - None - - {{ data.givenName }} -   - - - None - - {{ data.surname }} -   - - - None - - {{ data.displayName }} -   - - - None - - {{ data.mailNickname }} -   - - - None - - {{ data.mail }} -   - - - None - - {{ data.otherMails }} -   - - - None - - {{ data.proxyAddresses }} -   - - - None - - {{ data.jobTitle }} -   - - - None - - {{ data.officeLocation }} -   - - - None - - {{ data.value }} -   - - - None - - {{ data.mobilePhone }} -   - - - None - - {{ data.businessPhones }} -   - - - None - - {{ data.preferredLanguage }} -   - - - None - - {{ data.state }} -   - - - None - - {{ data.postalCode }} -   - - - None
-
- {% endif %} - {% endfor %} -
- -{% endblock %} diff --git a/office365_run_query.html b/office365_run_query.html deleted file mode 100644 index 305c89a..0000000 --- a/office365_run_query.html +++ /dev/null @@ -1,269 +0,0 @@ -{% extends 'widgets/widget_template.html' %} -{% load custom_template %} -{% block custom_title_prop %} - {% if title_logo %} - style="background-size: auto 60%; background-position: 50%; background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');" - {% endif %} -{% endblock %} -{% block title1 %}{{ title1 }}{% endblock %} -{% block title2 %}{{ title2 }}{% endblock %} -{% block custom_tools %}{% endblock %} -{% block widget_content %} - - - -
- - {% for result in results %} - - {% if not result.data %} -

No data found

- {% else %} -
-

Action Parameters

- - - - - - {% if result.param.folder %} - - - - - {% endif %} - {% if result.param.get_folder_id %} - - - - - {% endif %} - {% if result.param.subject %} - - - - - {% endif %} - {% if result.param.body %} - - - - - {% endif %} - {% if result.param.sender %} - - - - - {% endif %} - {% if result.param.limit %} - - - - - {% endif %} - {% if result.param.query %} - - - - - {% endif %} - {% if result.param.internet_message_id %} - - - - - {% endif %} -
Email Address - - {{ result.param.email_address }} -   - - -
Folder - - {{ result.param.folder }} -   - - -
Get Folder ID{{ result.param.get_folder_id }}
Subject - - {{ result.param.subject }} -   - - -
Body{{ result.param.body }}
Sender - - {{ result.param.sender }} -   - - -
Limit{{ result.param.limit }}
Query{{ result.param.query }}
Internet Message ID - - {{ result.param.internet_message_id }} -   - - -
-
-
-

Data

- - - - - - - - - - {% for data in result.data %} - - {% if data.sender.emailAddress.address is not None %} - - {% else %} - - {% endif %} - - {% if data.subject is not None %} - - {% else %} - - {% endif %} - - - - - {% endfor %} -
SenderReceived DateTimeSubjectBody PerviewMessage IDInternet Message ID
- - {{ data.sender.emailAddress.address }} -   - - - None{{ data.receivedDateTime }} - - {{ data.subject }} -   - - - None{{ data.bodyPreview }} - - {{ data.id }} -   - - - - - {{ data.internetMessageId }} -   - - -
-
- {% endif %} - {% endfor %} -
- -{% endblock %} diff --git a/office365_view.py b/office365_view.py deleted file mode 100644 index 64b1496..0000000 --- a/office365_view.py +++ /dev/null @@ -1,112 +0,0 @@ -# File: office365_view.py -# -# Copyright (c) 2017-2025 Splunk Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -# either express or implied. See the License for the specific language governing permissions -# and limitations under the License. -def get_ctx_result(provides, result): - """Function that parses data. - - :param result: result - :param provides: action name - :return: response data - """ - - file_attachment = [] - item_attachment = [] - reference_attachment = [] - other_attachment = [] - - ctx_result = {} - - param = result.get_param() - summary = result.get_summary() - data = result.get_data() - - ctx_result["param"] = param - - if summary: - ctx_result["summary"] = summary - ctx_result["action"] = provides - if not data: - ctx_result["data"] = {} - return ctx_result - - if provides == "get email": - for result in data: - attachments = result.get("attachments", []) - - if not attachments: - break - - for attachment in attachments: - attachment_type = attachment.get("attachmentType", "") - if attachment_type == "#microsoft.graph.fileAttachment": - file_attachment.append(attachment) - elif attachment_type == "#microsoft.graph.itemAttachment": - item_attachment.append(attachment) - elif attachment_type == "#microsoft.graph.referenceAttachment": - reference_attachment.append(attachment) - else: - other_attachment.append(attachment) - - attachment_data = { - "file_attachment": file_attachment, - "item_attachment": item_attachment, - "reference_attachment": reference_attachment, - "other_attachment": other_attachment, - } - - result.update({"attachment_data": attachment_data}) - - ctx_result["data"] = data - - return ctx_result - - -def display_view(provides, all_app_runs, context): - """Function that displays view. - - :param provides: action name - :param context: context - :param all_app_runs: all app runs - :return: html page - """ - - context["results"] = results = [] - for summary, action_results in all_app_runs: - for result in action_results: - ctx_result = get_ctx_result(provides, result) - if not ctx_result: - continue - results.append(ctx_result) - - return_page = None - - if provides == "list events": - return_page = "office365_list_events.html" - - if provides == "get email": - return_page = "office365_get_email.html" - - if provides == "run query": - return_page = "office365_run_query.html" - - if provides == "get rule": - return_page = "office365_get_rule.html" - - if provides == "list rules": - return_page = "office365_list_rules.html" - - if provides == "resolve name": - return_page = "office365_resolve_name.html" - - return return_page diff --git a/process_email.py b/process_email.py deleted file mode 100644 index 1ab9e72..0000000 --- a/process_email.py +++ /dev/null @@ -1,1301 +0,0 @@ -# File: process_email.py -# -# Copyright (c) 2017-2025 Splunk Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software distributed under -# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, -# either express or implied. See the License for the specific language governing permissions -# and limitations under the License. -import email -import hashlib -import json -import mimetypes -import os -import re -import shutil -import socket -import tempfile -from collections import OrderedDict -from copy import deepcopy -from email.header import decode_header, make_header -from html import unescape - -import magic -import phantom.app as phantom -import phantom.rules as phantom_rules -import phantom.utils as ph_utils -from bs4 import BeautifulSoup, UnicodeDammit -from django.core.validators import URLValidator -from phantom.vault import Vault -from requests.structures import CaseInsensitiveDict - -from office365_consts import MSGOFFICE365_ERROR_MSG_UNAVAILABLE - - -_container_common = {"run_automation": False} # Don't run any playbooks, when this artifact is added - -_artifact_common = {"run_automation": False} # Don't run any playbooks, when this artifact is added - -FILE_EXTENSIONS = { - ".vmsn": ["os memory dump", "vm snapshot file"], - ".vmss": ["os memory dump", "vm suspend file"], - ".js": ["javascript"], - ".doc": ["doc"], - ".docx": ["doc"], - ".xls": ["xls"], - ".xlsx": ["xls"], -} - -MAGIC_FORMATS = [ - (re.compile("^PE.* Windows"), ["pe file", "hash"]), - (re.compile("^MS-DOS executable"), ["pe file", "hash"]), - (re.compile("^PDF "), ["pdf"]), - (re.compile("^MDMP crash"), ["process dump"]), - (re.compile("^Macromedia Flash"), ["flash"]), -] - -MSG_DEFAULT_ARTIFACT_COUNT = 100 -MSG_DEFAULT_CONTAINER_COUNT = 100 -HASH_FIXED_PHANTOM_VERSION = "2.0.201" - -PROC_EMAIL_JSON_FILES = "files" -PROC_EMAIL_JSON_BODIES = "bodies" -PROC_EMAIL_JSON_DATE = "date" -PROC_EMAIL_JSON_FROM = "from" -PROC_EMAIL_JSON_SUBJECT = "subject" -PROC_EMAIL_JSON_TO = "to" -PROC_EMAIL_JSON_START_TIME = "start_time" -PROC_EMAIL_JSON_EXTRACT_ATTACHMENTS = "extract_attachments" -PROC_EMAIL_JSON_EXTRACT_BODY = "add_body_to_header_artifacts" -PROC_EMAIL_JSON_EXTRACT_URLS = "extract_urls" -PROC_EMAIL_JSON_EXTRACT_IPS = "extract_ips" -PROC_EMAIL_JSON_EXTRACT_DOMAINS = "extract_domains" -PROC_EMAIL_JSON_EXTRACT_HASHES = "extract_hashes" -PROC_EMAIL_JSON_IPS = "ips" -PROC_EMAIL_JSON_HASHES = "hashes" -PROC_EMAIL_JSON_URLS = "urls" -PROC_EMAIL_JSON_DOMAINS = "domains" -PROC_EMAIL_JSON_MSG_ID = "message_id" -PROC_EMAIL_JSON_EMAIL_HEADERS = "email_headers" -PROC_EMAIL_CONTENT_TYPE_MSG = "message/rfc822" - -URI_REGEX = ( - r"([Hh][Tt][Tt][Pp][Ss]?:\/\/)((?:[:@\.\-_0-9]|[^ -@\[-\`\{-\~\s]|" - r"[\[\(][^\s\[\]\(\)]*[\]\)])+)((?:[\/\?]+(?:[^\[\'\"\(\{\)\]\}\s]|[\[\(][^\[\]\(\)]*[\]\)])*)*)[\/]?" -) -EMAIL_REGEX = r"\b[A-Z0-9._%+-]+@+[A-Z0-9.-]+\.[A-Z]{2,}\b" -EMAIL_REGEX2 = r'".*"@[A-Z0-9.-]+\.[A-Z]{2,}\b' -HASH_REGEX = r"\b[0-9a-fA-F]{32}\b|\b[0-9a-fA-F]{40}\b|\b[0-9a-fA-F]{64}\b" -IP_REGEX = r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" -IPV6_REGEX = r"\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|" -IPV6_REGEX += r"(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))" -IPV6_REGEX += ( - r"|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)" r"(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|" -) -IPV6_REGEX += ( - r"(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)" - r"(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|" -) -IPV6_REGEX += ( - r"(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)" - r"(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|" -) -IPV6_REGEX += ( - r"(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)" - r"(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|" -) -IPV6_REGEX += ( - r"(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)" - r"(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|" -) -IPV6_REGEX += ( - r"(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)" - r"(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*" -) - - -uri_regexc = re.compile(URI_REGEX) -email_regexc = re.compile(EMAIL_REGEX, re.IGNORECASE) -email_regexc2 = re.compile(EMAIL_REGEX2, re.IGNORECASE) -hash_regexc = re.compile(HASH_REGEX) -ip_regexc = re.compile(IP_REGEX) -ipv6_regexc = re.compile(IPV6_REGEX) - - -def _get_error_msg_from_exception(e): - """ - Get appropriate error message from the exception. - :param e: Exception object - :return: error message - """ - - error_code = None - error_msg = MSGOFFICE365_ERROR_MSG_UNAVAILABLE - - try: - if hasattr(e, "args"): - if len(e.args) > 1: - error_code = e.args[0] - error_msg = e.args[1] - elif len(e.args) == 1: - error_msg = e.args[0] - except Exception: - pass - - if not error_code: - error_text = f"Error Message: {error_msg}" - else: - error_text = f"Error Code: {error_code}. Error Message: {error_msg}" - - return error_text - - -class ProcessEmail: - def __init__(self, base_connector, config): - self._base_connector = base_connector - self._config = config - self._email_id_contains = list() - self._container = dict() - self._artifacts = list() - self._attachments = list() - self._headers_from_ews = list() - self._attachments_from_msgraph = list() - self._parsed_mail = None - self._guid_to_hash = dict() - self._tmp_dirs = list() - self._trigger_automation = True - - def _get_file_contains(self, file_path): - contains = [] - ext = os.path.splitext(file_path)[1] - contains.extend(FILE_EXTENSIONS.get(ext, [])) - magic_str = magic.from_file(file_path) - for regex, cur_contains in MAGIC_FORMATS: - if regex.match(magic_str): - contains.extend(cur_contains) - - return contains - - def _is_ip(self, input_ip): - if ph_utils.is_ip(input_ip): - return True - - if self.is_ipv6(input_ip): - return True - - return False - - def is_ipv6(self, input_ip): - try: - socket.inet_pton(socket.AF_INET6, input_ip) - except Exception: # not a valid v6 address - return False - - return True - - def _debug_print(self, *args): - if self._base_connector and hasattr(self._base_connector, "debug_print"): - self._base_connector.debug_print(*args) - - return - - def _get_string(self, input_str, charset): - try: - if input_str: - input_str = UnicodeDammit(input_str).unicode_markup.encode(charset).decode(charset) - except Exception: - try: - input_str = str(make_header(decode_header(input_str))) - except Exception: - input_str = self._decode_uni_string(input_str, input_str) - self._debug_print(f"Error occurred while converting to string with specific encoding {input_str}") - return input_str - - def _clean_url(self, url): - url = url.strip("\r\n") - - # Check before splicing, find returns -1 if not found - # _and_ you will end up splicing on -1 (incorrectly) - if "<" in url: - url = url[: url.find("<")] - - if ">" in url: - url = url[: url.find(">")] - - return url.strip() - - def _extract_urls_domains(self, file_data, urls, domains, parent_id=None): - if not self._config[PROC_EMAIL_JSON_EXTRACT_DOMAINS] and not self._config[PROC_EMAIL_JSON_EXTRACT_URLS]: - return - - # Get domains from email - extracted_domains = set() - emails = [] - emails.extend(re.findall(email_regexc, file_data)) - emails.extend(re.findall(email_regexc2, file_data)) - - for curr_email in emails: - domain = curr_email[curr_email.rfind("@") + 1 :] - if domain and (not ph_utils.is_ip(domain)): - extracted_domains.add(domain) - - # try to load the email - try: - soup = BeautifulSoup(file_data, "html.parser") - except Exception as e: - error_msg = _get_error_msg_from_exception(e) - self._debug_print(f"Error occurred while parsing email data. {error_msg}") - return - - uris = [] - # get all tags that have hrefs and srcs - links = soup.find_all(href=True) - srcs = soup.find_all(src=True) - - uri_text = [] - - if links or srcs: - for link in links: - # work on the text part of the link, they might be http links different from the href - # and were either missed by the uri_regexc while parsing text or there was no text counterpart - # in the email - uri_text.append(self._clean_url(link.get_text())) - # it's html, so get all the urls - if not link["href"].startswith("mailto:"): - uris.append(link["href"]) - - for src in srcs: - uri_text.append(self._clean_url(src.get_text())) - # it's html, so get all the urls - uris.append(src["src"]) - - if uri_text: - uri_text = [x for x in uri_text if x.startswith("http")] - if uri_text: - uris.extend(uri_text) - - else: - file_data = unescape(file_data) - # Parse it as a text file - uris_from_text = [self._clean_url(uri.group(0)) for uri in re.finditer(uri_regexc, file_data)] - uris.extend(uris_from_text) - - # Get unique uris - unique_uris = set(uris) - - # Validate the uris - validate_uri = URLValidator(schemes=["http", "https"]) - validated_uris = list() - for uri in unique_uris: - try: - validate_uri(uri) - validated_uris.append(uri) - except Exception: - pass - - if self._config[PROC_EMAIL_JSON_EXTRACT_URLS]: - # add the uris to the urls - for uri in validated_uris: - if parent_id: - uri_dict = {"requestURL": uri, "parentInternetMessageId": parent_id} - else: - uri_dict = {"requestURL": uri} - urls.append(uri_dict) - - if self._config[PROC_EMAIL_JSON_EXTRACT_DOMAINS]: - for uri in validated_uris: - domain = phantom.get_host_from_url(uri) - if domain and not self._is_ip(domain): - extracted_domains.add(domain) - # work on any mailto urls if present - if links: - mailtos = [x["href"] for x in links if (x["href"].startswith("mailto:"))] - for curr_email in mailtos: - domain = curr_email[curr_email.find("@") + 1 :] - if domain and (not self._is_ip(domain)): - if "?" in domain: - domain = domain[: domain.find("?")] - extracted_domains.add(domain) - - for domain in extracted_domains: - if parent_id: - domains.append({"destinationDnsDomain": domain, "parentInternetMessageId": parent_id}) - else: - domains.append({"destinationDnsDomain": domain}) - - return - - def _sanitize_file_name(self, file_name): - return re.sub("[,\"']", "", file_name) - - def _get_ips(self, file_data, ips, parent_id=None): - if not self._config[PROC_EMAIL_JSON_EXTRACT_IPS]: - return None - - # First extract what looks like an IP from the file, this is a faster operation - ips_in_mail = re.findall(ip_regexc, file_data) - ip6_in_mail = re.findall(ipv6_regexc, file_data) - - if ip6_in_mail: - for ip6_tuple in ip6_in_mail: - ip6s = [x for x in ip6_tuple if x] - ips_in_mail.extend(ip6s) - - # Now validate them - if ips_in_mail: - ips_in_mail = set(ips_in_mail) - # match it with a slower and difficult regex. - # TODO: Fix this with a one step approach. - ips_in_mail = [x for x in ips_in_mail if self._is_ip(x)] - if ips_in_mail: - unique_ips = set(ips_in_mail) - for ip in unique_ips: - if parent_id: - ips.append({"sourceAddress": ip, "parentInternetMessageId": parent_id}) - else: - ips.append({"sourceAddress": ip}) - - def _sanitize_dict(self, obj): - if isinstance(obj, str): - return obj.replace("\x00", "") - if isinstance(obj, list): - return [self._sanitize_dict(item) for item in obj] - if isinstance(obj, set): - return {self._sanitize_dict(item) for item in obj} - if isinstance(obj, dict): - return {k: self._sanitize_dict(v) for k, v in obj.items()} - - return obj - - def _extract_hashes(self, file_data, hashes, parent_id=None): - if not self._config[PROC_EMAIL_JSON_EXTRACT_HASHES]: - return None - - hashs_in_mail = re.findall(hash_regexc, file_data) - if hashs_in_mail: - unique_hashes = set(hashs_in_mail) - for hash in unique_hashes: - if parent_id: - hashes.append({"fileHash": hash, "parentInternetMessageId": parent_id}) - else: - hashes.append({"fileHash": hash}) - - def _handle_body(self, body, parsed_mail, body_index, email_id): - local_file_path = body["file_path"] - charset = body.get("charset") - parent_id = None - - if "True" in local_file_path: - for item in parsed_mail["email_headers"]: - parent_id = item["cef"].get("parentInternetMessageId") - if parent_id: - break - - ips = parsed_mail[PROC_EMAIL_JSON_IPS] - hashes = parsed_mail[PROC_EMAIL_JSON_HASHES] - urls = parsed_mail[PROC_EMAIL_JSON_URLS] - domains = parsed_mail[PROC_EMAIL_JSON_DOMAINS] - - file_data = None - with open(local_file_path, "rb") as f: - file_data = f.read() - self._base_connector.debug_print("Reading file data using binary mode") - file_data = self._get_string(file_data, "utf-8") - - if file_data is None or len(file_data) == 0: - return phantom.APP_ERROR - - self._parse_email_headers_as_inline(file_data, parsed_mail, charset, email_id) - - self._extract_urls_domains(file_data, urls, domains, parent_id) - - self._get_ips(file_data, ips, parent_id) - - self._extract_hashes(file_data, hashes, parent_id) - - return phantom.APP_SUCCESS - - def _add_artifacts(self, input_set, artifact_name, start_index, artifacts): - added_artifacts = 0 - input_set = self._sanitize_dict(input_set) - for item in input_set: - # ignore empty entries - if not item: - continue - - artifact = {} - artifact.update(_artifact_common) - artifact["source_data_identifier"] = start_index + added_artifacts - artifact["cef"] = item - artifact["name"] = artifact_name - self._debug_print("Artifact:", artifact) - artifacts.append(artifact) - added_artifacts += 1 - - return added_artifacts - - def _parse_email_headers_as_inline(self, file_data, parsed_mail, charset, email_id): - # remove the 'Forwarded Message' from the email text and parse it - p = re.compile(r".*Forwarded Message.*\r\n(.*)", re.IGNORECASE) - email_text = p.sub(r"\1", file_data.strip()[:500]) - mail = email.message_from_string(email_text) - - self._parse_email_headers(parsed_mail, mail, charset, add_email_id=email_id) - - return phantom.APP_SUCCESS - - def _add_email_header_artifacts(self, email_header_artifacts, start_index, artifacts): - added_artifacts = 0 - for artifact in email_header_artifacts: - artifact["source_data_identifier"] = start_index + added_artifacts - artifacts.append(artifact) - added_artifacts += 1 - - return added_artifacts - - def _create_artifacts(self, parsed_mail, ingest_email=True): - # get all the artifact data in their own list objects - ips = [dict(t) for t in set([tuple(d.items()) for d in parsed_mail[PROC_EMAIL_JSON_IPS]])] - hashes = [dict(t) for t in set([tuple(d.items()) for d in parsed_mail[PROC_EMAIL_JSON_HASHES]])] - urls = [dict(t) for t in set([tuple(d.items()) for d in parsed_mail[PROC_EMAIL_JSON_URLS]])] - domains = [dict(t) for t in set([tuple(d.items()) for d in parsed_mail[PROC_EMAIL_JSON_DOMAINS]])] - email_headers = parsed_mail[PROC_EMAIL_JSON_EMAIL_HEADERS] - - # set the default artifact dict - - artifact_id = 0 - - # add artifacts - added_artifacts = self._add_artifacts(ips, "IP Artifact", artifact_id, self._artifacts) - artifact_id += added_artifacts - - added_artifacts = self._add_artifacts(hashes, "Hash Artifact", artifact_id, self._artifacts) - artifact_id += added_artifacts - - added_artifacts = self._add_artifacts(urls, "URL Artifact", artifact_id, self._artifacts) - artifact_id += added_artifacts - - added_artifacts = self._add_artifacts(domains, "Domain Artifact", artifact_id, self._artifacts) - artifact_id += added_artifacts - - if ingest_email: - added_artifacts = self._add_email_header_artifacts(email_headers, artifact_id, self._artifacts) - artifact_id += added_artifacts - - return phantom.APP_SUCCESS - - def _decode_uni_string(self, input_str, def_name): - # try to find all the decoded strings, we could have multiple decoded strings - # or a single decoded string between two normal strings separated by \r\n - # YEAH...it could get that messy - encoded_strings = re.findall(r"=\?.*\?=", input_str, re.I) - - # return input_str as is, no need to do any conversion - if not encoded_strings: - return input_str - - # get the decoded strings - try: - decoded_strings = [decode_header(x)[0] for x in encoded_strings] - decoded_strings = [{"value": x[0], "encoding": x[1]} for x in decoded_strings] - except Exception as e: - error_msg = _get_error_msg_from_exception(e) - self._debug_print(f"Decoding: {encoded_strings}. {error_msg}") - return def_name - - # convert to dict for safe access, if it's an empty list, the dict will be empty - decoded_strings = dict(enumerate(decoded_strings)) - - new_str = "" - new_str_create_count = 0 - for i, encoded_string in enumerate(encoded_strings): - decoded_string = decoded_strings.get(i) - - if not decoded_string: - # nothing to replace with - continue - - value = decoded_string.get("value") - encoding = decoded_string.get("encoding") - - if not encoding or not value: - # nothing to replace with - continue - - try: - # Some non-ascii characters were causing decoding issue with - # the UnicodeDammit and working correctly with the decode function. - # keeping previous logic in the except block incase of failure. - if value: - value = value.decode(encoding) - new_str += value - new_str_create_count += 1 - except Exception: - try: - if encoding != "utf-8": - value = str(value, encoding) - except Exception: - pass - try: - # commenting the existing approach due to a new approach being deployed below - # substitute the encoded string with the decoded one - # input_str = input_str.replace(encoded_string, value) - - # make new string instead of replacing in the input string because issue find in PAPP-9531 - if value: - new_str += UnicodeDammit(value).unicode_markup - new_str_create_count += 1 - except Exception: - pass - - # replace input string with new string because issue find in PAPP-9531 - if new_str and new_str_create_count == len(encoded_strings): - self._debug_print("Creating a new string entirely from the encoded_strings and assigning into input_str") - input_str = new_str - - return input_str - - def _get_container_name(self, parsed_mail, email_id): - # Create the default name - def_cont_name = f"Email ID: {email_id}" - - # get the subject from the parsed mail - subject = parsed_mail.get(PROC_EMAIL_JSON_SUBJECT) - - # if no subject then return the default - if not subject: - return def_cont_name - - return self._decode_uni_string(subject, def_cont_name) - - def _handle_if_body(self, content_disp, content_id, content_type, part, bodies, file_path): - content_charset = part.get_content_charset() - process_as_body = False - - # if content disposition is None then assume that it is - if content_disp is None: - process_as_body = True - # if content disposition is inline - elif content_disp.lower().strip() == "inline": - if "text/html" in content_type or "text/plain" in content_type: - process_as_body = True - - if not process_as_body: - return (phantom.APP_SUCCESS, True) - - part_payload = part.get_payload(decode=True) - - if not part_payload: - return (phantom.APP_SUCCESS, False) - - if "text/html" in content_type and self._parsed_mail[PROC_EMAIL_JSON_EMAIL_HEADERS]: - # Get Email Header Artifact - email_header_artifact = self._parsed_mail[PROC_EMAIL_JSON_EMAIL_HEADERS][0] - artifact_cef = email_header_artifact["cef"] - html_body = self._get_string(part_payload, charset=content_charset) - artifact_cef["bodyHtml"] = html_body - - try: - soup = BeautifulSoup(html_body, "html.parser") - # Remove the script, style, footer, title and navigation part from the HTML message - for element in soup(["script", "style", "footer", "title", "nav"]): - element.extract() - body_text = soup.get_text(separator=" ") - split_lines = body_text.split("\n") - split_lines = [x.strip() for x in split_lines if x.strip()] - body_text = "\n".join(split_lines) - if body_text: - artifact_cef["bodyText"] = body_text - except Exception: - self._debug_print("Cannot parse email body text details") - - with open(file_path, "wb") as f: - f.write(part_payload) - - bodies.append({"file_path": file_path, "charset": part.get_content_charset()}) - - return (phantom.APP_SUCCESS, False) - - def _handle_attachment(self, part, tmp_dir, file_name, file_path): - files = self._parsed_mail[PROC_EMAIL_JSON_FILES] - - if not self._config[PROC_EMAIL_JSON_EXTRACT_ATTACHMENTS]: - return phantom.APP_SUCCESS - - part_base64_encoded = part.get_payload() - - email_headers = self._get_email_headers_from_part(part) - - attach_meta_info = dict() - - if email_headers: - attach_meta_info = {"headers": dict(email_headers)} - - for curr_attach in self._attachments_from_msgraph: - if curr_attach.get("should_ignore", False): - continue - - try: - attach_content = curr_attach["content"] - except Exception: - continue - - if attach_content.strip().replace("\r\n", "") == part_base64_encoded.strip().replace("\r\n", ""): - attach_meta_info.update(dict(curr_attach)) - del attach_meta_info["content"] - curr_attach["should_ignore"] = True - - part_payload = part.get_payload(decode=True) - if not part_payload: - return phantom.APP_SUCCESS - - try: - with open(file_path, "wb") as f: - f.write(part_payload) - files.append({"file_name": file_name, "file_path": file_path, "meta_info": attach_meta_info}) - except OSError as ioerr: - error_msg = _get_error_msg_from_exception(ioerr) - if "File name too long" in error_msg: - self.write_with_new_filename(part_payload, files, file_name, as_byte=False) - else: - self._debug_print(f"Failed to write file: {ioerr}") - - def write_with_new_filename(self, data, dict_to_fill, file_name, as_byte=False): - try: - fd, full_path = tempfile.mkstemp(dir=Vault.get_vault_tmp_dir()) - os.close(fd) - - with open(full_path, "wb") as f: - if as_byte: - f.write(data.as_bytes()) - else: - f.write(data) - dict_to_fill.append({"file_name": file_name, "file_path": full_path}) - except Exception as e: - self._base_connector.debug_print(f"Exception while writing file: {e}") - - def _handle_part(self, part, part_index, tmp_dir, extract_attach, parsed_mail, child=False): - bodies = parsed_mail[PROC_EMAIL_JSON_BODIES] - - # get the file_name - file_name = part.get_filename() - content_disp = part.get("Content-Disposition") - content_type = part.get("Content-Type") - content_id = part.get("Content-ID") - - if file_name is None: - # init name and extension to default values - name = f"part_{part_index}_{child}" - extension = f".{part_index}" - - # Try to create an extension from the content type if possible - if content_type is not None: - extension = mimetypes.guess_extension(re.sub(";.*", "", content_type)) - - # Try to create a name from the content id if possible - if content_id is not None: - name = content_id - - file_name = f"{name}{extension}" - else: - file_name = self._sanitize_file_name(self._decode_uni_string(file_name, file_name)) - - # Remove any chars that we don't want in the name - file_path = "{}/{}_{}_{}".format(tmp_dir, part_index, file_name.replace("<", "").replace(">", "").replace(" ", ""), child) - - self._debug_print(f"file_path: {file_path}") - - # is the part representing the body of the email - _status, process_further = self._handle_if_body(content_disp, content_id, content_type, part, bodies, file_path) - - if not process_further: - return phantom.APP_SUCCESS - - # is this another email as an attachment - if content_type is not None and content_type.find(PROC_EMAIL_CONTENT_TYPE_MSG) != -1: - return phantom.APP_SUCCESS - - # This is an attachment and it's not an email - self._handle_attachment(part, tmp_dir, file_name, file_path) - - return phantom.APP_SUCCESS - - def _update_headers(self, headers_to_update): - # compare the various values of the passed header (param: headers) - # to the header that the class got self._headers_from_ews - if not self._headers_from_ews: - return phantom.APP_SUCCESS - - if not headers_to_update: - return phantom.APP_SUCCESS - - headers_ci = CaseInsensitiveDict(headers_to_update) - - for curr_header_lower in self._headers_from_ews: - if headers_ci.get("message-id", "default_value1").strip() == curr_header_lower.get("message-id", "default_value2").strip(): - # the headers match with the one that we got from the ews API, so update it - headers_to_update.update(curr_header_lower) - - return phantom.APP_SUCCESS - - def _get_email_headers_from_part(self, part, charset=None): - email_headers = list(part.items()) - - # TODO: the next 2 ifs can be condensed to use 'or' - if charset is None: - charset = part.get_content_charset() - - if charset is None: - charset = "utf8" - - if not email_headers: - return {} - - # Convert the header tuple into a dictionary - email_headers_dict = CaseInsensitiveDict() - try: - [email_headers_dict.update({x[0]: self._get_string(x[1], charset)}) for x in email_headers] - except Exception as e: - error_msg = _get_error_msg_from_exception(e) - err = "Error occurred while converting the header tuple into a dictionary" - self._debug_print(f"{err}. {error_msg}") - - # Handle received seperately - received_headers = list() - try: - received_headers = [self._get_string(x[1], charset) for x in email_headers if x[0].lower() == "received"] - except Exception as e: - error_msg = _get_error_msg_from_exception(e) - err = "Error occurred while handling the received header tuple separately" - self._debug_print(f"{err}. {error_msg}") - - if received_headers: - email_headers_dict["Received"] = received_headers - - # handle the subject string, if required add a new key - subject = email_headers_dict.get("Subject") - if subject: - if isinstance(subject, str): - email_headers_dict["decodedSubject"] = self._decode_uni_string(subject, subject) - - return email_headers_dict - - def _parse_email_headers(self, parsed_mail, part, charset=None, add_email_id=None): - email_header_artifacts = parsed_mail[PROC_EMAIL_JSON_EMAIL_HEADERS] - - email_headers = self._get_email_headers_from_part(part, charset) - - if not email_headers: - return 0 - - # Parse email keys first - cef_artifact = {} - cef_types = {} - - if email_headers.get("From"): - emails = email_headers["From"] - if emails: - cef_artifact.update({"fromEmail": emails}) - - if email_headers.get("To"): - emails = email_headers["To"] - if emails: - cef_artifact.update({"toEmail": emails}) - - # if the header did not contain any email addresses then ignore this artifact - message_id = email_headers.get("message-id") - if not cef_artifact and message_id is None: - return 0 - - cef_types.update({"fromEmail": ["email"], "toEmail": ["email"]}) - - if email_headers: - self._update_headers(email_headers) - cef_artifact["emailHeaders"] = dict(email_headers) - - for curr_key in list(cef_artifact["emailHeaders"].keys()): - if curr_key.lower().startswith("body"): - curr_value = cef_artifact["emailHeaders"].pop(curr_key) - if self._config.get(PROC_EMAIL_JSON_EXTRACT_BODY, False): - cef_artifact.update({curr_key: curr_value}) - elif curr_key == "parentInternetMessageId": - curr_value = cef_artifact["emailHeaders"].pop(curr_key) - cef_artifact.update({curr_key: curr_value}) - elif curr_key == "parentGuid": - curr_value = cef_artifact["emailHeaders"].pop(curr_key) - cef_artifact.update({curr_key: curr_value}) - elif curr_key == "emailGuid": - curr_value = cef_artifact["emailHeaders"].pop(curr_key) - cef_artifact.update({curr_key: curr_value}) - - # Adding the email id as a cef artifact crashes the UI when trying to show the action dialog box - # so not adding this right now. All the other code to process the emailId is there, but the refraining - # from adding the emailId - # add_email_id = False - if add_email_id: - cef_artifact["emailId"] = add_email_id - if self._email_id_contains: - cef_types.update({"emailId": self._email_id_contains}) - - artifact = {} - artifact.update(_artifact_common) - artifact["name"] = "Email Artifact" - artifact["label"] = "email" - artifact["severity"] = self._base_connector.get_config().get("container_severity", "medium") - artifact["cef"] = cef_artifact - artifact["cef_types"] = cef_types - email_header_artifacts.append(artifact) - - return len(email_header_artifacts) - - def _handle_mail_object(self, mail, email_id, rfc822_email, tmp_dir, start_time_epoch, ingest_email=True): - self._parsed_mail = OrderedDict() - - # Create a tmp directory for this email, will extract all files here - if not os.path.exists(tmp_dir): - os.makedirs(tmp_dir) - - extract_attach = self._config[PROC_EMAIL_JSON_EXTRACT_ATTACHMENTS] - - charset = mail.get_content_charset() - - if charset is None: - charset = "utf-8" - - # Extract fields and place it in a dictionary - self._parsed_mail[PROC_EMAIL_JSON_SUBJECT] = mail.get("Subject", "") - self._parsed_mail[PROC_EMAIL_JSON_FROM] = mail.get("From", "") - self._parsed_mail[PROC_EMAIL_JSON_TO] = mail.get("To", "") - self._parsed_mail[PROC_EMAIL_JSON_DATE] = mail.get("Date", "") - self._parsed_mail[PROC_EMAIL_JSON_MSG_ID] = mail.get("Message-ID", "") - self._parsed_mail[PROC_EMAIL_JSON_FILES] = files = [] - self._parsed_mail[PROC_EMAIL_JSON_BODIES] = bodies = [] - self._parsed_mail[PROC_EMAIL_JSON_START_TIME] = start_time_epoch - self._parsed_mail[PROC_EMAIL_JSON_EMAIL_HEADERS] = [] - - # parse the parts of the email - if mail.is_multipart(): - child = False - message_id = None - for i, part in enumerate(mail.walk()): - add_email_id = None - if i == 0: - add_email_id = email_id - - self._parse_email_headers(self._parsed_mail, part, add_email_id=add_email_id) - if message_id is None and part.get("Message-ID"): - message_id = part.get("Message-ID") - child = False - elif message_id and part.get("Message-ID"): - child = True - - self._debug_print(f"part: {part.__dict__}") - self._debug_print("part type", type(part)) - if part.is_multipart(): - continue - try: - ret_val = self._handle_part(part, i, tmp_dir, extract_attach, self._parsed_mail, child) - except Exception as e: - self._debug_print(f"ErrorExp in _handle_part # {i}", e) - continue - - if phantom.is_fail(ret_val): - continue - - else: - self._parse_email_headers(self._parsed_mail, mail, add_email_id=email_id) - file_path = f"{tmp_dir}/part_1.text" - with open(file_path, "wb") as f: - f.write(mail.get_payload(decode=True)) - bodies.append({"file_path": file_path, "charset": mail.get_content_charset()}) - - # get the container name - container_name = self._get_container_name(self._parsed_mail, email_id) - - if container_name is None: - return phantom.APP_ERROR - - # Add the container - # first save the container, to do that copy things from parsed_mail to a new object - container = {} - container_data = dict(self._parsed_mail) - - # delete the header info, we dont make it a part of the container json - del container_data[PROC_EMAIL_JSON_EMAIL_HEADERS] - container.update(_container_common) - self._container["source_data_identifier"] = email_id - self._container["name"] = container_name - self._container["data"] = {"raw_email": rfc822_email} - - # Create the sets before handling the bodies If both the bodies add the same ip - # only one artifact should be created - - # Changing to a list due to add parent identifier in each artifact - self._parsed_mail[PROC_EMAIL_JSON_IPS] = list() - self._parsed_mail[PROC_EMAIL_JSON_HASHES] = list() - self._parsed_mail[PROC_EMAIL_JSON_URLS] = list() - self._parsed_mail[PROC_EMAIL_JSON_DOMAINS] = list() - - # For bodies - for i, body in enumerate(bodies): - if not body: - continue - - try: - self._handle_body(body, self._parsed_mail, i, email_id) - except Exception as e: - self._debug_print(f"ErrorExp in _handle_body # {i}: {e!s}") - continue - - # Files - self._attachments.extend(files) - - self._create_artifacts(self._parsed_mail, ingest_email=ingest_email) - - return phantom.APP_SUCCESS - - def _set_email_id_contains(self, email_id): - if not self._base_connector: - return - - try: - email_id = self._get_string(email_id, "utf-8") - except Exception: - email_id = str(email_id) - - if ph_utils.is_sha1(email_id): - self._email_id_contains = ["vault id"] - else: - self._email_id_contains = ["msgoffice365 message id"] - return - - def _int_process_email(self, rfc822_email, email_id, start_time_epoch, ingest_email=True): - mail = email.message_from_string(rfc822_email) - - ret_val = phantom.APP_SUCCESS - - tmp_dir = tempfile.mkdtemp(prefix="ph_email_phmsgo365") - self._tmp_dirs.append(tmp_dir) - - try: - ret_val = self._handle_mail_object(mail, email_id, rfc822_email, tmp_dir, start_time_epoch, ingest_email=ingest_email) - except Exception as e: - msg = f"ErrorExp in self._handle_mail_object: {e}" - self._debug_print(msg) - return (phantom.APP_ERROR, msg, []) - - results = [{"container": self._container, "artifacts": self._artifacts, "files": self._attachments, "temp_directory": tmp_dir}] - - return (ret_val, "Email Parsed", results) - - def process_email(self, rfc822_email, email_id, epoch, container_id=None, email_headers=None, attachments_data=None, ingest_email=True): - if email_headers: - for curr_header in email_headers: - self._headers_from_ews.append(CaseInsensitiveDict(curr_header)) - - if self._config[PROC_EMAIL_JSON_EXTRACT_ATTACHMENTS] and attachments_data is not None: - self._attachments_from_msgraph = attachments_data - - try: - self._set_email_id_contains(email_id) - except Exception: - pass - - ret_val, msg, results = self._int_process_email(rfc822_email, email_id, epoch, ingest_email=ingest_email) - - if not ret_val: - self._del_tmp_dirs() - return (phantom.APP_ERROR, msg) - - try: - self._parse_results(results, container_id) - except Exception as e: - # delete any temp directories that were created by the email parsing function - self._del_tmp_dirs() - self._base_connector.debug_print(_get_error_msg_from_exception(e)) - return phantom.APP_ERROR, "Error occurred while parsing the results" - - return (phantom.APP_SUCCESS, "Email Processed") - - def _save_ingested(self, container, using_dummy): - if using_dummy: - cid = container["id"] - artifacts = container["artifacts"] - for artifact in artifacts: - artifact["container_id"] = cid - ret_val, msg, _ids = self._base_connector.save_artifacts(artifacts) - self._base_connector.debug_print(f"save_artifacts returns, value: {ret_val}, reason: {msg}") - - else: - ret_val, msg, cid = self._base_connector.save_container(container) - self._base_connector.debug_print(f"save_container (with artifacts) returns, value: {ret_val}, reason: {msg}, id: {cid}") - - return ret_val, msg, cid - - def _handle_save_ingested(self, artifacts, container, container_id, files): - # One of either container or container_id will be set to None - using_dummy = False - - if container_id: - # We are adding artifacts to an existing container - using_dummy = True - container = { - "name": "Dummy Container", - "dummy": True, - "id": container_id, - "artifacts": artifacts, - } - else: - # Create a new container - container["artifacts"] = artifacts - - if hasattr(self._base_connector, "_preprocess_container"): - container = self._base_connector._preprocess_container(container) - - for artifact in list([x for x in container.get("artifacts", []) if not x.get("source_data_identifier")]): - self._set_sdi(artifact) - - if files and container.get("artifacts"): - # Make sure the playbook only runs once - # We will instead set run_automation on the last vault artifact which is added - container["artifacts"][-1]["run_automation"] = False - - ret_val, msg, container_id = self._save_ingested(container, using_dummy) - - if phantom.is_fail(ret_val): - msg = f"Failed to save ingested artifacts, error msg: {msg}" - self._base_connector.debug_print(msg) - return - - if not container_id: - msg = "save_container did not return a container_id" - self._base_connector.debug_print(msg) - return - - vault_artifacts_added = 0 - - last_file = len(files) - 1 - for i, curr_file in enumerate(files): - run_automation = self._trigger_automation if i == last_file else False - ret_val, added_to_vault = self._handle_file(curr_file, container_id, run_automation) - - if added_to_vault: - vault_artifacts_added += 1 - - return - - def _parse_results(self, results, container_id=None): - param = self._base_connector.get_current_param() - - container_count = MSG_DEFAULT_CONTAINER_COUNT - - if param: - container_count = param.get(phantom.APP_JSON_CONTAINER_COUNT, MSG_DEFAULT_CONTAINER_COUNT) - - results = results[:container_count] - - for result in results: - if container_id is None: - container = result.get("container") - - if not container: - continue - - container.update(_container_common) - - else: - container = None - - # run a loop to first set the sdi which will create the hash - artifacts = result.get("artifacts", []) - for j, artifact in enumerate(artifacts): - if not artifact: - continue - - self._set_sdi(artifact) - - if not artifacts: - continue - - len_artifacts = len(artifacts) - - for j, artifact in enumerate(artifacts): - if not artifact: - continue - - # if it is the last artifact of the last container - if (j + 1) == len_artifacts: - # mark it such that active playbooks get executed if trigger automation is set to True - artifact["run_automation"] = self._trigger_automation - - cef_artifact = artifact.get("cef") - if "parentGuid" in cef_artifact: - parent_guid = cef_artifact.pop("parentGuid") - if parent_guid in self._guid_to_hash: - cef_artifact["parentSourceDataIdentifier"] = self._guid_to_hash[parent_guid] - if "emailGuid" in cef_artifact: - del cef_artifact["emailGuid"] - - self._handle_save_ingested(artifacts, container, container_id, result.get("files")) - - # delete any temp directories that were created by the email parsing function - [shutil.rmtree(x["temp_directory"], ignore_errors=True) for x in results if x.get("temp_directory")] - - return self._base_connector.set_status(phantom.APP_SUCCESS) - - def _add_vault_hashes_to_dictionary(self, cef_artifact, vault_id): - _success, _msg, vault_info = phantom_rules.vault_info(vault_id=vault_id) - - if not vault_info: - return (phantom.APP_ERROR, "Vault ID not found") - - # The return value is a list, each item represents an item in the vault - # matching the vault id, the info that we are looking for (the hashes) - # will be the same for every entry, so just access the first one - try: - metadata = vault_info[0].get("metadata") - except Exception: - return (phantom.APP_ERROR, "Failed to get vault item metadata") - - try: - cef_artifact["fileHashSha256"] = metadata["sha256"] - except Exception: - pass - - try: - cef_artifact["fileHashMd5"] = metadata["md5"] - except Exception: - pass - - try: - cef_artifact["fileHashSha1"] = metadata["sha1"] - except Exception: - pass - - return (phantom.APP_SUCCESS, "Mapped hash values") - - def _handle_file(self, curr_file, container_id, run_automation=False): - file_name = curr_file.get("file_name") - - local_file_path = curr_file["file_path"] - - contains = self._get_file_contains(local_file_path) - - # lets move the data into the vault - vault_attach_dict = {} - - if not file_name: - file_name = os.path.basename(local_file_path) - - self._base_connector.debug_print(f"Vault file name: {file_name}") - - vault_attach_dict[phantom.APP_JSON_ACTION_NAME] = self._base_connector.get_action_name() - vault_attach_dict[phantom.APP_JSON_APP_RUN_ID] = self._base_connector.get_app_run_id() - - vault_add_success = False - vault_add_msg = "" - vault_id = None - - file_name = self._sanitize_file_name(self._decode_uni_string(file_name, file_name)) - - try: - vault_add_success, vault_add_msg, vault_id = phantom_rules.vault_add( - file_location=local_file_path, container=container_id, file_name=file_name, metadata=vault_attach_dict - ) - except Exception as e: - self._debug_print(phantom.APP_ERR_FILE_ADD_TO_VAULT.format(e)) - return (phantom.APP_ERROR, phantom.APP_ERROR) - - if not vault_add_success: - self._debug_print(f"Failed to add file to Vault: {vault_add_msg}") - return (phantom.APP_ERROR, phantom.APP_ERROR) - - # add the vault id artifact to the container - cef_artifact = curr_file.get("meta_info", {}) - if file_name: - cef_artifact.update({"fileName": file_name}) - - if vault_id: - cef_artifact.update({"vaultId": vault_id}) - - # now get the rest of the hashes and add them to the cef artifact - self._add_vault_hashes_to_dictionary(cef_artifact, vault_id) - - if not cef_artifact: - return (phantom.APP_SUCCESS, phantom.APP_ERROR) - - artifact = {} - artifact.update(_artifact_common) - artifact["container_id"] = container_id - artifact["name"] = "Vault Artifact" - artifact["cef"] = cef_artifact - artifact["run_automation"] = run_automation - if contains: - artifact["cef_types"] = {"vaultId": contains} - self._set_sdi(artifact) - - if "parentGuid" in cef_artifact: - parent_guid = cef_artifact.pop("parentGuid") - cef_artifact["parentSourceDataIdentifier"] = self._guid_to_hash[parent_guid] - - ret_val, status_string, artifact_id = self._base_connector.save_artifact(artifact) - self._base_connector.debug_print(f"save_artifact returns, value: {ret_val}, reason: {status_string}, id: {artifact_id}") - - return (phantom.APP_SUCCESS, ret_val) - - def _set_sdi(self, input_dict): - if "source_data_identifier" in input_dict: - del input_dict["source_data_identifier"] - - input_dict_hash = input_dict - - cef = input_dict.get("cef") - - curr_email_guid = None - - if cef is not None: - if "parentGuid" in cef or "emailGuid" in cef: - # make a copy since the dictionary will have to be different - input_dict_hash = deepcopy(input_dict) - cef = input_dict_hash["cef"] - if "parentGuid" in cef: - del cef["parentGuid"] - curr_email_guid = cef.get("emailGuid") - if curr_email_guid is not None: - del cef["emailGuid"] - - input_dict["source_data_identifier"] = self._create_dict_hash(input_dict_hash) - - if curr_email_guid: - self._guid_to_hash[curr_email_guid] = input_dict["source_data_identifier"] - - return phantom.APP_SUCCESS - - def _create_dict_hash(self, input_dict): - input_dict_str = None - - if not input_dict: - return None - - try: - input_dict_str = json.dumps(input_dict, sort_keys=True) - except Exception as e: - self._base_connector.debug_print("Handled exception in _create_dict_hash", e) - return None - - fips_enabled = self._base_connector._get_fips_enabled() - if not fips_enabled: - return hashlib.md5(UnicodeDammit(input_dict_str).unicode_markup.encode("utf-8")).hexdigest() # nosemgrep - - return hashlib.sha256(UnicodeDammit(input_dict_str).unicode_markup.encode("utf-8")).hexdigest() - - def _del_tmp_dirs(self): - """Remove any tmp_dirs that were created.""" - for tmp_dir in self._tmp_dirs: - shutil.rmtree(tmp_dir, ignore_errors=True) diff --git a/pyproject.toml b/pyproject.toml index a816e31..0f3102c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,60 @@ +[project] +name = "msgraphoffice365" +version = "4.1.0" +description = "This app enables MS Graph API-based email ingestion and investigative actions on Office 365" +license = "Copyright (c) 2017-2025 Splunk Inc." +requires-python = ">=3.13, <3.15" +authors = [] +dependencies = [ + "beautifulsoup4>=4.13.0,<4.14.0", + "msal>=1.35.1", + "requests>=2.32.5", + "splunk-soar-sdk[email]>=3.18.0", +] + +# [tool.uv.sources] +# splunk-soar-sdk = { path = "../soar-apps-sdk", editable = true } + +[tool.soar.app] +main_module = "src.app:app" + +### YOU SHOULD NOT NEED TO TOUCH ANYTHING BELOW THIS LINE ### + +[dependency-groups] +dev = [ + "coverage>=7.6.7,<8", + "mypy>=1.2.0,<2", + "pre-commit>=4.2.0,<5", + "pytest>=7.4.2,<8", + "pytest-mock>=3.14.0,<4", + "pytest-watch>=4.2.0,<5", + "ruff>=0.11.6,<1", +] + +[[tool.uv.index]] +url = "https://pypi.python.org/simple" + +[tool.uv] +environments = [ + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.13'", + "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.13'", + "sys_platform == 'darwin' and platform_machine == 'x86_64' and python_version == '3.13'", + "sys_platform == 'darwin' and platform_machine == 'arm64' and python_version == '3.13'", + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.14'", + "sys_platform == 'linux' and platform_machine == 'aarch64' and python_version == '3.14'", + "sys_platform == 'darwin' and platform_machine == 'x86_64' and python_version == '3.14'", + "sys_platform == 'darwin' and platform_machine == 'arm64' and python_version == '3.14'", +] +required-environments = [ + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.13'", + "sys_platform == 'linux' and platform_machine == 'x86_64' and python_version == '3.14'", +] + # Ruff linting [tool.ruff] -line-length = 145 -target-version = "py39" +output-format = "full" +fix = true +target-version = "py313" [tool.ruff.lint] select = [ # Auto-fixable rules only diff --git a/release_notes/unreleased.md b/release_notes/unreleased.md index fbcb2fd..6b2c2a9 100644 --- a/release_notes/unreleased.md +++ b/release_notes/unreleased.md @@ -1 +1,5 @@ **Unreleased** + +* Migrated app to Splunk SOAR SDK +* Updated to support Python 3.13 +* Modernized app, structure, and dependencies diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 5a609ea..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -msal==1.34.0 diff --git a/__init__.py b/src/__init__.py similarity index 87% rename from __init__.py rename to src/__init__.py index c8a2c5b..66abd01 100644 --- a/__init__.py +++ b/src/__init__.py @@ -1,6 +1,4 @@ -# 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. @@ -12,3 +10,8 @@ # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, # either express or implied. See the License for the specific language governing permissions # and limitations under the License. + +from . import ( + actions, + app, +) diff --git a/src/actions/__init__.py b/src/actions/__init__.py new file mode 100644 index 0000000..ac79088 --- /dev/null +++ b/src/actions/__init__.py @@ -0,0 +1,40 @@ +# 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. + +from . import ( + block_sender, + copy_email, + create_folder, + delete_email, + delete_event, + generate_token, + get_email, + get_email_properties, + get_folder_id, + get_mailbox_messages, + get_rule, + list_events, + list_folders, + list_group_members, + list_groups, + list_rules, + list_users, + move_email, + oof_check, + report_message, + resolve_name, + run_query, + send_email, + unblock_sender, + update_email, +) diff --git a/src/actions/block_sender.py b/src/actions/block_sender.py new file mode 100644 index 0000000..a0678a0 --- /dev/null +++ b/src/actions/block_sender.py @@ -0,0 +1,50 @@ +# Copyright (c) 2017-2026 Splunk Inc. +import json + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class BlockSenderParams(Params): + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + sender: str = Param( + description="Email address of sender to block", + required=True, + cef_types=["email"], + ) + + +class BlockSenderOutput(ActionOutput): + message: str | None = None + + +@app.action( + description="Add a sender to the blocked senders list", action_type="contain" +) +def block_sender( + params: BlockSenderParams, soar: SOARClient, asset: Asset +) -> BlockSenderOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.email_address}/mailFolders/junkemail/messageRules" + body = { + "displayName": f"Block sender: {params.sender}", + "sequence": 1, + "isEnabled": True, + "conditions": {"senderContains": [params.sender]}, + "actions": {"delete": True, "stopProcessingRules": True}, + } + + helper.make_rest_call_helper(endpoint, method="post", data=json.dumps(body)) + + soar.set_message(f"Successfully blocked sender: {params.sender}") + return BlockSenderOutput(message=f"Successfully blocked sender: {params.sender}") diff --git a/src/actions/copy_email.py b/src/actions/copy_email.py new file mode 100644 index 0000000..4ce191d --- /dev/null +++ b/src/actions/copy_email.py @@ -0,0 +1,62 @@ +# Copyright (c) 2017-2026 Splunk Inc. +import json + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class CopyEmailParams(Params): + id: str = Param( + description="Message ID to copy", + required=True, + primary=True, + cef_types=["msgoffice365 message id"], + ) + email_address: str = Param( + description="User's email (mailbox to copy from)", + required=True, + cef_types=["email"], + ) + folder: str = Param( + description="Destination folder name/path or ID", + required=True, + cef_types=["msgoffice365 folder id"], + ) + get_folder_id: bool = Param( + description="Retrieve folder ID from folder name/path", + required=False, + default=True, + ) + + +class CopyEmailOutput(ActionOutput): + id: str | None = None + subject: str | None = None + parentFolderId: str | None = None + + +@app.action(description="Copy an email to a folder", action_type="generic") +def copy_email( + params: CopyEmailParams, soar: SOARClient, asset: Asset +) -> CopyEmailOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + folder_id = params.folder + if params.get_folder_id: + resolved_id = helper.get_folder_id(params.folder, params.email_address) + if resolved_id: + folder_id = resolved_id + else: + raise ValueError(f"Could not resolve folder: {params.folder}") + + endpoint = f"/users/{params.email_address}/messages/{params.id}/copy" + body = {"DestinationId": folder_id} + resp = helper.make_rest_call_helper(endpoint, method="post", data=json.dumps(body)) + + soar.set_message("Successfully copied email") + return CopyEmailOutput(**resp) diff --git a/src/actions/create_folder.py b/src/actions/create_folder.py new file mode 100644 index 0000000..90a2f39 --- /dev/null +++ b/src/actions/create_folder.py @@ -0,0 +1,69 @@ +# Copyright (c) 2017-2026 Splunk Inc. +import json + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class CreateFolderParams(Params): + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + folder: str = Param( + description="Name of the folder to create", + required=True, + ) + parent_folder: str = Param( + description="Parent folder name/path or ID (leave empty for root)", + required=False, + default="", + ) + get_folder_id: bool = Param( + description="Retrieve parent folder ID from folder name/path", + required=False, + default=True, + ) + + +class CreateFolderOutput(ActionOutput): + id: str | None = None + displayName: str | None = None + parentFolderId: str | None = None + childFolderCount: int | None = None + totalItemCount: int | None = None + + +@app.action(description="Create a new mail folder", action_type="generic") +def create_folder( + params: CreateFolderParams, soar: SOARClient, asset: Asset +) -> CreateFolderOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + parent_folder_id = None + if params.parent_folder: + if params.get_folder_id: + parent_folder_id = helper.get_folder_id( + params.parent_folder, params.email_address + ) + else: + parent_folder_id = params.parent_folder + + if parent_folder_id: + endpoint = ( + f"/users/{params.email_address}/mailFolders/{parent_folder_id}/childFolders" + ) + else: + endpoint = f"/users/{params.email_address}/mailFolders" + + body = {"displayName": params.folder} + resp = helper.make_rest_call_helper(endpoint, method="post", data=json.dumps(body)) + + soar.set_message(f"Successfully created folder: {params.folder}") + return CreateFolderOutput(**resp) diff --git a/src/actions/delete_email.py b/src/actions/delete_email.py new file mode 100644 index 0000000..df17db2 --- /dev/null +++ b/src/actions/delete_email.py @@ -0,0 +1,39 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class DeleteEmailParams(Params): + id: str = Param( + description="Message ID to delete", + required=True, + primary=True, + cef_types=["msgoffice365 message id"], + ) + email_address: str = Param( + description="User's email (mailbox to delete from)", + required=True, + cef_types=["email"], + ) + + +class DeleteEmailOutput(ActionOutput): + message: str | None = None + + +@app.action(description="Delete an email", action_type="generic") +def delete_email( + params: DeleteEmailParams, soar: SOARClient, asset: Asset +) -> DeleteEmailOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.email_address}/messages/{params.id}" + helper.make_rest_call_helper(endpoint, method="delete") + + soar.set_message("Email deleted successfully") + return DeleteEmailOutput(message="Email deleted successfully") diff --git a/src/actions/delete_event.py b/src/actions/delete_event.py new file mode 100644 index 0000000..746b98e --- /dev/null +++ b/src/actions/delete_event.py @@ -0,0 +1,53 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class DeleteEventParams(Params): + event_id: str = Param( + description="Event ID to delete", + required=True, + primary=True, + cef_types=["msgoffice365 event id"], + ) + user_id: str = Param( + description="User ID/Principal name", + required=False, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + default="", + ) + group_id: str = Param( + description="Group ID", + required=False, + cef_types=["msgoffice365 group id"], + default="", + ) + + +class DeleteEventOutput(ActionOutput): + message: str | None = None + + +@app.action(description="Delete an event", action_type="generic") +def delete_event( + params: DeleteEventParams, soar: SOARClient, asset: Asset +) -> DeleteEventOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + if not params.user_id and not params.group_id: + raise ValueError("Either user_id or group_id must be provided") + + if params.user_id: + endpoint = f"/users/{params.user_id}/events/{params.event_id}" + else: + endpoint = f"/groups/{params.group_id}/events/{params.event_id}" + + helper.make_rest_call_helper(endpoint, method="delete") + + soar.set_message("Event deleted successfully") + return DeleteEventOutput(message="Event deleted successfully") diff --git a/src/actions/generate_token.py b/src/actions/generate_token.py new file mode 100644 index 0000000..0e84ed1 --- /dev/null +++ b/src/actions/generate_token.py @@ -0,0 +1,30 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class GenerateTokenParams(Params): + pass + + +class GenerateTokenOutput(ActionOutput): + message: str | None = None + + +@app.action( + description="Generates a new access token", + action_type="generic", +) +def generate_token( + params: GenerateTokenParams, soar: SOARClient, asset: Asset +) -> GenerateTokenOutput: + helper = MsGraphHelper(soar, asset) + helper._access_token = None + helper.get_token() + + soar.set_message("Token generated successfully") + return GenerateTokenOutput(message="Token generated successfully") diff --git a/src/actions/get_email.py b/src/actions/get_email.py new file mode 100644 index 0000000..ff64432 --- /dev/null +++ b/src/actions/get_email.py @@ -0,0 +1,200 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +import json +from typing import TYPE_CHECKING + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + + +if TYPE_CHECKING: + from ..app import Asset +from ..helper import MsGraphHelper, serialize_complex_fields + + +class GetEmailParams(Params): + id: str = Param( + description="Message ID to get", + required=True, + primary=True, + cef_types=["msgoffice365 message id"], + ) + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + get_headers: bool = Param( + description="Get email headers", + required=False, + default=True, + ) + download_attachments: bool = Param( + description="Download attachments", + required=False, + default=False, + ) + download_email: bool = Param( + description="Download email as EML file", + required=False, + default=False, + ) + + +class GetEmailOutput(ActionOutput): + id: str | None = None + subject: str | None = None + body: str | None = None + bodyPreview: str | None = None + sender: str | None = None + from_field: str | None = None + toRecipients: str | None = None + ccRecipients: str | None = None + bccRecipients: str | None = None + receivedDateTime: str | None = None + sentDateTime: str | None = None + hasAttachments: bool | None = None + importance: str | None = None + isRead: bool | None = None + internetMessageId: str | None = None + internetMessageHeaders: str | None = None + attachments: str | None = None + event_id: str | None = None + + +COMPLEX_EMAIL_FIELDS = [ + "body", + "sender", + "toRecipients", + "ccRecipients", + "bccRecipients", + "internetMessageHeaders", + "attachments", +] + + +def _parse_json_field(val): + if not val: + return None + if isinstance(val, str): + try: + return json.loads(val) + except (json.JSONDecodeError, ValueError): + return None + return val + + +def _categorize_attachments(attachments_json): + attachments = _parse_json_field(attachments_json) + if not attachments: + return None + file_att, item_att, ref_att, other_att = [], [], [], [] + for att in attachments: + att_type = att.get("attachmentType", att.get("@odata.type", "")) + if att_type == "#microsoft.graph.fileAttachment": + file_att.append(att) + elif att_type == "#microsoft.graph.itemAttachment": + item_att.append(att) + elif att_type == "#microsoft.graph.referenceAttachment": + ref_att.append(att) + else: + other_att.append(att) + return { + "file_attachment": file_att, + "item_attachment": item_att, + "reference_attachment": ref_att, + "other_attachment": other_att, + } + + +def _extract_email_address(json_str, path=("emailAddress", "address")): + data = _parse_json_field(json_str) + if not data: + return None + for key in path: + if isinstance(data, dict): + data = data.get(key) + else: + return None + return data + + +def _extract_recipient_addresses(json_str): + data = _parse_json_field(json_str) + if not data or not isinstance(data, list): + return [] + return [ + r.get("emailAddress", {}).get("address", "") + for r in data + if r.get("emailAddress", {}).get("address") + ] + + +def render_get_email(output: list[GetEmailOutput]) -> dict: + results = [] + for item in output: + from_addr = _extract_email_address(item.from_field) or _extract_email_address( + item.sender + ) + to_addrs = _extract_recipient_addresses(item.toRecipients) + attachment_data = _categorize_attachments(item.attachments) + + emails = [ + { + "id": item.id, + "subject": item.subject, + "body_preview": item.bodyPreview, + "from_address": from_addr, + "to_addresses": to_addrs, + "has_attachments": item.hasAttachments, + "internet_message_id": item.internetMessageId, + "event_id": item.event_id, + } + ] + + results.append( + { + "data": True, + "param_id": item.id, + "param_email_address": None, + "param_download_attachments": None, + "param_get_headers": None, + "emails": emails, + "has_attachments": item.hasAttachments, + "attachment_data": attachment_data, + } + ) + return {"results": results} + + +def get_email( + params: GetEmailParams, soar: SOARClient, asset: "Asset" +) -> GetEmailOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.email_address}/messages/{params.id}" + resp = helper.make_rest_call_helper(endpoint) + + if params.get_headers: + header_endpoint = f"{endpoint}?$select=internetMessageHeaders" + header_resp = helper.make_rest_call_helper(header_endpoint) + headers = header_resp.get("internetMessageHeaders", []) + resp["internetMessageHeaders"] = ( + {h["name"]: h["value"] for h in headers} if headers else {} + ) + + if params.download_attachments and resp.get("hasAttachments"): + attach_endpoint = f"{endpoint}/attachments" + attach_resp = helper.make_rest_call_helper(attach_endpoint) + resp["attachments"] = attach_resp.get("value", []) + + resp["from_field"] = resp.pop("from", None) + resp["event_id"] = ( + resp.get("event", {}).get("id") if isinstance(resp.get("event"), dict) else None + ) + + resp = serialize_complex_fields(resp, [*COMPLEX_EMAIL_FIELDS, "from_field"]) + soar.set_message("Successfully retrieved email") + return GetEmailOutput(**resp) diff --git a/src/actions/get_email_properties.py b/src/actions/get_email_properties.py new file mode 100644 index 0000000..ad2514d --- /dev/null +++ b/src/actions/get_email_properties.py @@ -0,0 +1,118 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper, serialize_complex_fields + + +class GetEmailPropertiesParams(Params): + id: str = Param( + description="Message ID to get", + required=True, + primary=True, + cef_types=["msgoffice365 message id"], + ) + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + get_headers: bool = Param( + description="Get email headers", + required=False, + default=True, + ) + get_body: bool = Param( + description="Get email body", + required=False, + default=True, + ) + get_unique_body: bool = Param( + description="Get unique body (without previous replies)", + required=False, + default=False, + ) + get_sender: bool = Param( + description="Get sender information", + required=False, + default=True, + ) + download_attachments: bool = Param( + description="Download attachments", + required=False, + default=False, + ) + + +class GetEmailPropertiesOutput(ActionOutput): + id: str | None = None + subject: str | None = None + body: str | None = None + uniqueBody: str | None = None + bodyPreview: str | None = None + sender: str | None = None + toRecipients: str | None = None + ccRecipients: str | None = None + receivedDateTime: str | None = None + hasAttachments: bool | None = None + internetMessageHeaders: str | None = None + attachments: str | None = None + + +COMPLEX_EMAIL_PROPS_FIELDS = [ + "body", + "uniqueBody", + "sender", + "toRecipients", + "ccRecipients", + "internetMessageHeaders", + "attachments", +] + + +@app.action(description="Get properties of an email", action_type="investigate") +def get_email_properties( + params: GetEmailPropertiesParams, soar: SOARClient, asset: Asset +) -> GetEmailPropertiesOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + select_fields = [ + "id", + "subject", + "bodyPreview", + "receivedDateTime", + "hasAttachments", + "toRecipients", + "ccRecipients", + ] + if params.get_body: + select_fields.append("body") + if params.get_unique_body: + select_fields.append("uniqueBody") + if params.get_sender: + select_fields.extend(["sender", "from"]) + + endpoint = f"/users/{params.email_address}/messages/{params.id}" + api_params = {"$select": ",".join(select_fields)} + resp = helper.make_rest_call_helper(endpoint, params=api_params) + + if params.get_headers: + header_endpoint = f"{endpoint}?$select=internetMessageHeaders" + header_resp = helper.make_rest_call_helper(header_endpoint) + headers = header_resp.get("internetMessageHeaders", []) + resp["internetMessageHeaders"] = ( + {h["name"]: h["value"] for h in headers} if headers else {} + ) + + if params.download_attachments and resp.get("hasAttachments"): + attach_endpoint = f"{endpoint}/attachments" + attach_resp = helper.make_rest_call_helper(attach_endpoint) + resp["attachments"] = attach_resp.get("value", []) + + resp = serialize_complex_fields(resp, COMPLEX_EMAIL_PROPS_FIELDS) + soar.set_message("Successfully retrieved email properties") + return GetEmailPropertiesOutput(**resp) diff --git a/src/actions/get_folder_id.py b/src/actions/get_folder_id.py new file mode 100644 index 0000000..0644ffa --- /dev/null +++ b/src/actions/get_folder_id.py @@ -0,0 +1,50 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class GetFolderIdParams(Params): + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + primary=True, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + ) + folder: str = Param( + description="Folder name or path (e.g. 'Inbox' or 'Inbox/Subfolder')", + required=True, + ) + + +class GetFolderIdOutput(ActionOutput): + folder_id: str | None = None + folder_name: str | None = None + display_name: str | None = None + parent_folder_id: str | None = None + + +@app.action(description="Get the ID of a mail folder", action_type="investigate") +def get_folder_id( + params: GetFolderIdParams, soar: SOARClient, asset: Asset +) -> GetFolderIdOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + folder_id = helper.get_folder_id(params.folder, params.email_address) + if not folder_id: + raise ValueError(f"Could not find folder: {params.folder}") + + endpoint = f"/users/{params.email_address}/mailFolders/{folder_id}" + resp = helper.make_rest_call_helper(endpoint) + + soar.set_message(f"Successfully retrieved folder ID: {folder_id}") + return GetFolderIdOutput( + folder_id=resp.get("id"), + folder_name=params.folder, + display_name=resp.get("displayName"), + parent_folder_id=resp.get("parentFolderId"), + ) diff --git a/src/actions/get_mailbox_messages.py b/src/actions/get_mailbox_messages.py new file mode 100644 index 0000000..1976b19 --- /dev/null +++ b/src/actions/get_mailbox_messages.py @@ -0,0 +1,94 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..consts import MSGOFFICE365_PER_PAGE_COUNT, MSGOFFICE365_SELECT_PARAMETER_LIST +from ..helper import MsGraphHelper, serialize_complex_fields + + +class GetMailboxMessagesParams(Params): + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + folder: str = Param( + description="Folder name/path or ID", + required=False, + default="Inbox", + ) + get_folder_id: bool = Param( + description="Retrieve folder ID from folder name/path", + required=False, + default=True, + ) + limit: int = Param( + description="Maximum number of messages to return", + required=False, + default=100, + ) + offset: int = Param( + description="Number of messages to skip", + required=False, + default=0, + ) + + +class MessageOutput(ActionOutput): + id: str | None = None + subject: str | None = None + sender: str | None = None + receivedDateTime: str | None = None + bodyPreview: str | None = None + hasAttachments: bool | None = None + isRead: bool | None = None + importance: str | None = None + + +@app.action(description="Get messages from a mailbox folder", action_type="investigate") +def get_mailbox_messages( + params: GetMailboxMessagesParams, soar: SOARClient, asset: Asset +) -> list[MessageOutput]: + if params.limit is not None and params.limit <= 0: + raise ValueError("'limit' action parameter must be a positive integer") + + helper = MsGraphHelper(soar, asset) + helper.get_token() + + folder_id = params.folder + if params.get_folder_id and params.folder: + resolved_id = helper.get_folder_id(params.folder, params.email_address) + if resolved_id: + folder_id = resolved_id + + endpoint = f"/users/{params.email_address}/mailFolders/{folder_id}/messages" + select_fields = ",".join(MSGOFFICE365_SELECT_PARAMETER_LIST) + api_params = { + "$select": select_fields, + "$top": str(min(params.limit, MSGOFFICE365_PER_PAGE_COUNT)), + "$orderby": "receivedDateTime desc", + } + + if params.offset > 0: + api_params["$skip"] = str(params.offset) + + messages = [] + next_link = None + while len(messages) < params.limit: + resp = helper.make_rest_call_helper( + endpoint, params=api_params, nextLink=next_link + ) + messages.extend(resp.get("value", [])) + + next_link = resp.get("@odata.nextLink") + if not next_link or len(messages) >= params.limit: + break + api_params = None + + messages = messages[: params.limit] + messages = [serialize_complex_fields(m, ["sender"]) for m in messages] + soar.set_message(f"Successfully retrieved {len(messages)} messages") + return [MessageOutput(**m) for m in messages] diff --git a/src/actions/get_rule.py b/src/actions/get_rule.py new file mode 100644 index 0000000..b14870c --- /dev/null +++ b/src/actions/get_rule.py @@ -0,0 +1,96 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +import json +from typing import TYPE_CHECKING + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + + +if TYPE_CHECKING: + from ..app import Asset +from ..helper import MsGraphHelper, serialize_complex_fields + + +class GetRuleParams(Params): + user_id: str = Param( + description="User ID/Principal name", + required=True, + primary=True, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + ) + rule_id: str = Param( + description="Inbox rule ID", + required=True, + primary=True, + cef_types=["msgoffice365 rule id"], + ) + + +class GetRuleOutput(ActionOutput): + id: str | None = None + displayName: str | None = None + sequence: int | None = None + isEnabled: bool | None = None + isReadOnly: bool | None = None + hasError: bool | None = None + conditions: str | None = None + actions: str | None = None + + +def _parse_json_field(val): + if not val: + return {} + if isinstance(val, str): + try: + return json.loads(val) + except (json.JSONDecodeError, ValueError): + return {} + return val if isinstance(val, dict) else {} + + +def _extract_keyed_items(data, keyword): + if not isinstance(data, dict): + return {} + return {k: v for k, v in data.items() if keyword in k.lower()} + + +def render_get_rule(output: list[GetRuleOutput]) -> dict: + rules = [] + for item in output: + actions_data = _parse_json_field(item.actions) + conditions_data = _parse_json_field(item.conditions) + rules.append( + { + "display_name": item.displayName, + "action_items": actions_data if isinstance(actions_data, dict) else {}, + "condition_items": conditions_data + if isinstance(conditions_data, dict) + else {}, + } + ) + + results = [ + { + "data": bool(rules), + "user_id": None, + "rule_id": None, + "rules": rules, + } + ] + return {"results": results} + + +def get_rule(params: GetRuleParams, soar: SOARClient, asset: "Asset") -> GetRuleOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = ( + f"/users/{params.user_id}/mailFolders/inbox/messageRules/{params.rule_id}" + ) + resp = helper.make_rest_call_helper(endpoint) + + resp = serialize_complex_fields(resp, ["conditions", "actions"]) + soar.set_message("Successfully retrieved specified inbox rule") + return GetRuleOutput(**resp) diff --git a/src/actions/list_events.py b/src/actions/list_events.py new file mode 100644 index 0000000..cd4dffb --- /dev/null +++ b/src/actions/list_events.py @@ -0,0 +1,158 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +import json +from typing import TYPE_CHECKING + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + + +if TYPE_CHECKING: + from ..app import Asset +from ..helper import MsGraphHelper, serialize_complex_fields + + +class ListEventsParams(Params): + user_id: str = Param( + description="User ID/Principal name", + required=False, + primary=True, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + default="", + ) + group_id: str = Param( + description="Group ID", + required=False, + primary=True, + cef_types=["msgoffice365 group id"], + default="", + ) + filter: str = Param( + description="OData query to filter/search for specific results", + required=False, + default="", + ) + limit: int = Param( + description="Maximum number of events to return", required=False, default=0 + ) + + +class EventOutput(ActionOutput): + id: str | None = None + subject: str | None = None + bodyPreview: str | None = None + start: str | None = None + end: str | None = None + location: str | None = None + organizer: str | None = None + attendees: str | None = None + attendee_list: str | None = None + isAllDay: bool | None = None + isCancelled: bool | None = None + webLink: str | None = None + + +COMPLEX_EVENT_FIELDS = ["start", "end", "location", "organizer", "attendees"] + + +def _parse_json_field(val): + if not val: + return None + if isinstance(val, str): + try: + return json.loads(val) + except (json.JSONDecodeError, ValueError): + return None + return val + + +def render_list_events(output: list[EventOutput]) -> dict: + events = [] + for item in output: + start_data = _parse_json_field(item.start) + end_data = _parse_json_field(item.end) + events.append( + { + "id": item.id, + "subject": item.subject, + "start_time": start_data.get("dateTime") + if isinstance(start_data, dict) + else None, + "end_time": end_data.get("dateTime") + if isinstance(end_data, dict) + else None, + "attendee_list": item.attendee_list, + } + ) + + results = [ + { + "data": bool(events), + "user_id": None, + "group_id": None, + "limit": None, + "filter": None, + "events": events, + } + ] + return {"results": results} + + +def list_events( + params: ListEventsParams, soar: SOARClient, asset: "Asset" +) -> list[EventOutput]: + if params.limit is not None and params.limit < 0: + raise ValueError("'limit' action parameter must be a non-negative integer") + + helper = MsGraphHelper(soar, asset) + helper.get_token() + + if not params.user_id and not params.group_id: + raise ValueError("Either user_id or group_id must be provided") + + if params.user_id: + endpoint = f"/users/{params.user_id}/events" + else: + endpoint = f"/groups/{params.group_id}/events" + + api_params = {} + if params.filter: + if params.filter.startswith("$"): + api_params.update( + dict( + item.split("=", 1) + for item in params.filter.split("&") + if "=" in item + ) + ) + else: + api_params["$filter"] = params.filter + if params.limit and params.limit > 0: + api_params["$top"] = str(params.limit) + + events = [] + next_link = None + while True: + resp = helper.make_rest_call_helper( + endpoint, params=api_params if api_params else None, nextLink=next_link + ) + for event in resp.get("value", []): + attendees = event.get("attendees", []) + event["attendee_list"] = ", ".join( + a.get("emailAddress", {}).get("name", "") for a in attendees + ) + events.append(event) + + if params.limit and len(events) >= params.limit: + events = events[: params.limit] + break + + next_link = resp.get("@odata.nextLink") + if not next_link: + break + api_params = None + + events = [serialize_complex_fields(e, COMPLEX_EVENT_FIELDS) for e in events] + soar.set_message(f"Successfully retrieved {len(events)} events") + return [EventOutput(**e) for e in events] diff --git a/src/actions/list_folders.py b/src/actions/list_folders.py new file mode 100644 index 0000000..92420cc --- /dev/null +++ b/src/actions/list_folders.py @@ -0,0 +1,64 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class ListFoldersParams(Params): + user_id: str = Param( + description="User ID/Principal name", + required=True, + primary=True, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + ) + folder_id: str = Param( + description="Parent mail folder id or well-known name", + required=False, + default="", + cef_types=["msgoffice365 folder id"], + ) + + +class FolderOutput(ActionOutput): + id: str | None = None + displayName: str | None = None + parentFolderId: str | None = None + childFolderCount: int | None = None + unreadItemCount: int | None = None + totalItemCount: int | None = None + + +class ListFoldersSummary(ActionOutput): + total_folders_returned: int = 0 + + +@app.action(description="Get the mail folder hierarchy", action_type="investigate") +def list_folders( + params: ListFoldersParams, soar: SOARClient, asset: Asset +) -> list[FolderOutput]: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + if params.folder_id: + endpoint = ( + f"/users/{params.user_id}/mailFolders/{params.folder_id}/childFolders" + ) + else: + endpoint = f"/users/{params.user_id}/mailFolders" + + folders = [] + next_link = None + while True: + resp = helper.make_rest_call_helper(endpoint, nextLink=next_link) + folders.extend(resp.get("value", [])) + + next_link = resp.get("@odata.nextLink") + if not next_link: + break + + soar.set_message(f"Successfully retrieved {len(folders)} folders") + soar.set_summary(ListFoldersSummary(total_folders_returned=len(folders))) + return [FolderOutput(**f) for f in folders] diff --git a/src/actions/list_group_members.py b/src/actions/list_group_members.py new file mode 100644 index 0000000..87f270d --- /dev/null +++ b/src/actions/list_group_members.py @@ -0,0 +1,63 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class ListGroupMembersParams(Params): + group_id: str = Param( + description="Group ID", + required=True, + primary=True, + cef_types=["msgoffice365 group id"], + ) + limit: int = Param( + description="Maximum number of members to return", required=False, default=0 + ) + + +class GroupMemberOutput(ActionOutput): + id: str | None = None + displayName: str | None = None + mail: str | None = None + userPrincipalName: str | None = None + userType: str | None = None + + +@app.action(description="Get group members", action_type="investigate") +def list_group_members( + params: ListGroupMembersParams, soar: SOARClient, asset: Asset +) -> list[GroupMemberOutput]: + if params.limit is not None and params.limit < 0: + raise ValueError("'limit' action parameter must be a non-negative integer") + + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/groups/{params.group_id}/members" + api_params = {} + if params.limit and params.limit > 0: + api_params["$top"] = str(params.limit) + + members = [] + next_link = None + while True: + resp = helper.make_rest_call_helper( + endpoint, params=api_params if api_params else None, nextLink=next_link + ) + members.extend(resp.get("value", [])) + + if params.limit and len(members) >= params.limit: + members = members[: params.limit] + break + + next_link = resp.get("@odata.nextLink") + if not next_link: + break + api_params = None + + soar.set_message(f"Successfully retrieved {len(members)} members") + return [GroupMemberOutput(**m) for m in members] diff --git a/src/actions/list_groups.py b/src/actions/list_groups.py new file mode 100644 index 0000000..59b287f --- /dev/null +++ b/src/actions/list_groups.py @@ -0,0 +1,70 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class ListGroupsParams(Params): + filter: str = Param( + description="Search for specific results", required=False, default="" + ) + limit: int = Param( + description="Maximum number of groups to return", required=False, default=0 + ) + + +class GroupOutput(ActionOutput): + id: str | None = None + displayName: str | None = None + description: str | None = None + mail: str | None = None + mailEnabled: bool | None = None + mailNickname: str | None = None + groupTypes: list[str] | None = None + createdDateTime: str | None = None + + +@app.action( + description="List all the groups in an organization, including but not limited to Office 365 groups", + action_type="investigate", +) +def list_groups( + params: ListGroupsParams, soar: SOARClient, asset: Asset +) -> list[GroupOutput]: + if params.limit is not None and params.limit < 0: + raise ValueError("'limit' action parameter must be a non-negative integer") + + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = "/groups" + api_params = {} + if params.filter: + api_params["$filter"] = params.filter + if params.limit and params.limit > 0: + api_params["$top"] = str(params.limit) + + groups = [] + next_link = None + while True: + resp = helper.make_rest_call_helper( + endpoint, params=api_params if api_params else None, nextLink=next_link + ) + groups.extend(resp.get("value", [])) + + if params.limit and len(groups) >= params.limit: + groups = groups[: params.limit] + break + + next_link = resp.get("@odata.nextLink") + if not next_link: + break + api_params = None + + soar.set_message( + f"Successfully retrieved {len(groups)} {'group' if len(groups) == 1 else 'groups'}" + ) + return [GroupOutput(**g) for g in groups] diff --git a/src/actions/list_rules.py b/src/actions/list_rules.py new file mode 100644 index 0000000..feca3b7 --- /dev/null +++ b/src/actions/list_rules.py @@ -0,0 +1,84 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +import json +from typing import TYPE_CHECKING + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + + +if TYPE_CHECKING: + from ..app import Asset +from ..helper import MsGraphHelper, serialize_complex_fields + + +class ListRulesParams(Params): + user_id: str = Param( + description="User ID/Principal name", + required=True, + primary=True, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + ) + + +class RuleOutput(ActionOutput): + id: str | None = None + displayName: str | None = None + sequence: int | None = None + isEnabled: bool | None = None + isReadOnly: bool | None = None + hasError: bool | None = None + conditions: str | None = None + actions: str | None = None + + +def render_list_rules(output: list[RuleOutput]) -> dict: + rules = [] + for item in output: + actions_data = None + if item.actions: + try: + actions_data = ( + json.loads(item.actions) + if isinstance(item.actions, str) + else item.actions + ) + except (json.JSONDecodeError, ValueError): + actions_data = item.actions + + delete_enabled = None + if isinstance(actions_data, dict): + delete_enabled = actions_data.get("delete") + + rules.append( + { + "id": item.id, + "display_name": item.displayName, + "delete_enabled": delete_enabled, + "actions": actions_data, + } + ) + + results = [ + { + "data": bool(rules), + "rules": rules, + } + ] + return {"results": results} + + +def list_rules( + params: ListRulesParams, soar: SOARClient, asset: "Asset" +) -> list[RuleOutput]: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.user_id}/mailFolders/inbox/messageRules" + resp = helper.make_rest_call_helper(endpoint) + rules = resp.get("value", []) + + rules = [serialize_complex_fields(r, ["conditions", "actions"]) for r in rules] + soar.set_message(f"Successfully retrieved {len(rules)} rules") + return [RuleOutput(**r) for r in rules] diff --git a/src/actions/list_users.py b/src/actions/list_users.py new file mode 100644 index 0000000..581102e --- /dev/null +++ b/src/actions/list_users.py @@ -0,0 +1,69 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class ListUsersParams(Params): + filter: str = Param( + description="Search for specific results", required=False, default="" + ) + limit: int = Param( + description="Maximum number of users to return", required=False, default=0 + ) + + +class UserOutput(ActionOutput): + id: str | None = None + displayName: str | None = None + givenName: str | None = None + surname: str | None = None + userPrincipalName: str | None = None + mail: str | None = None + jobTitle: str | None = None + mobilePhone: str | None = None + officeLocation: str | None = None + businessPhones: list[str] | None = None + + +@app.action(description="Retrieve a list of users", action_type="investigate") +def list_users( + params: ListUsersParams, soar: SOARClient, asset: Asset +) -> list[UserOutput]: + if params.limit is not None and params.limit < 0: + raise ValueError("'limit' action parameter must be a non-negative integer") + + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = "/users" + api_params = {} + if params.filter: + api_params["$filter"] = params.filter + if params.limit and params.limit > 0: + api_params["$top"] = str(params.limit) + + users = [] + next_link = None + while True: + resp = helper.make_rest_call_helper( + endpoint, params=api_params if api_params else None, nextLink=next_link + ) + users.extend(resp.get("value", [])) + + if params.limit and len(users) >= params.limit: + users = users[: params.limit] + break + + next_link = resp.get("@odata.nextLink") + if not next_link: + break + api_params = None + + soar.set_message( + f"Successfully retrieved {len(users)} {'user' if len(users) == 1 else 'users'}" + ) + return [UserOutput(**u) for u in users] diff --git a/src/actions/move_email.py b/src/actions/move_email.py new file mode 100644 index 0000000..48147d4 --- /dev/null +++ b/src/actions/move_email.py @@ -0,0 +1,62 @@ +# Copyright (c) 2017-2026 Splunk Inc. +import json + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class MoveEmailParams(Params): + id: str = Param( + description="Message ID to move", + required=True, + primary=True, + cef_types=["msgoffice365 message id"], + ) + email_address: str = Param( + description="User's email (mailbox to move from)", + required=True, + cef_types=["email"], + ) + folder: str = Param( + description="Destination folder name/path or ID", + required=True, + cef_types=["msgoffice365 folder id"], + ) + get_folder_id: bool = Param( + description="Retrieve folder ID from folder name/path", + required=False, + default=True, + ) + + +class MoveEmailOutput(ActionOutput): + id: str | None = None + subject: str | None = None + parentFolderId: str | None = None + + +@app.action(description="Move an email to a folder", action_type="generic") +def move_email( + params: MoveEmailParams, soar: SOARClient, asset: Asset +) -> MoveEmailOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + folder_id = params.folder + if params.get_folder_id: + resolved_id = helper.get_folder_id(params.folder, params.email_address) + if resolved_id: + folder_id = resolved_id + else: + raise ValueError(f"Could not resolve folder: {params.folder}") + + endpoint = f"/users/{params.email_address}/messages/{params.id}/move" + body = {"DestinationId": folder_id} + resp = helper.make_rest_call_helper(endpoint, method="post", data=json.dumps(body)) + + soar.set_message("Successfully moved email") + return MoveEmailOutput(**resp) diff --git a/src/actions/oof_check.py b/src/actions/oof_check.py new file mode 100644 index 0000000..83fe0d0 --- /dev/null +++ b/src/actions/oof_check.py @@ -0,0 +1,41 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper, serialize_complex_fields + + +class OofCheckParams(Params): + user_id: str = Param( + description="User ID/Principal name", + required=True, + primary=True, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + ) + + +class OofCheckOutput(ActionOutput): + status: str | None = None + externalAudience: str | None = None + externalReplyMessage: str | None = None + internalReplyMessage: str | None = None + scheduledStartDateTime: str | None = None + scheduledEndDateTime: str | None = None + + +@app.action(description="Get user's out of office status", action_type="investigate") +def oof_check(params: OofCheckParams, soar: SOARClient, asset: Asset) -> OofCheckOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.user_id}/mailboxSettings/automaticRepliesSetting" + resp = helper.make_rest_call_helper(endpoint) + + resp = serialize_complex_fields( + resp, ["scheduledStartDateTime", "scheduledEndDateTime"] + ) + soar.set_message("Successfully retrieved out of office status") + return OofCheckOutput(**resp) diff --git a/src/actions/report_message.py b/src/actions/report_message.py new file mode 100644 index 0000000..ca0db75 --- /dev/null +++ b/src/actions/report_message.py @@ -0,0 +1,66 @@ +# Copyright (c) 2017-2026 Splunk Inc. +import json + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class ReportMessageParams(Params): + message_id: str = Param( + description="Message ID to pick the sender of", + required=True, + cef_types=["msgoffice365 message id"], + ) + user_id: str = Param( + description="User ID to base the action of", + required=True, + cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"], + ) + is_message_move_requested: bool = Param( + description="Indicates whether the message should be moved out of current folder", + required=False, + default=False, + ) + report_action: str = Param( + description="Indicates the type of action to be reported on the message", + required=True, + value_list=["junk", "notJunk", "phish", "unknown", "unknownFutureValue"], + ) + + +class ReportMessageOutput(ActionOutput): + message: str | None = None + + +@app.action( + description="Add the sender email into the report", + action_type="contain", +) +def report_message( + params: ReportMessageParams, soar: SOARClient, asset: Asset +) -> ReportMessageOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.user_id}/messages/{params.message_id}/markAsJunk" + + body = { + "moveToJunk": params.is_message_move_requested + if params.report_action == "junk" + else False, + } + + if params.report_action in ["junk", "phish"]: + helper.make_rest_call_helper(endpoint, method="post", data=json.dumps(body)) + elif params.report_action == "notJunk": + body["moveToJunk"] = False + helper.make_rest_call_helper(endpoint, method="post", data=json.dumps(body)) + + soar.set_message(f"Successfully reported message as {params.report_action}") + return ReportMessageOutput( + message=f"Successfully reported message as {params.report_action}" + ) diff --git a/src/actions/resolve_name.py b/src/actions/resolve_name.py new file mode 100644 index 0000000..eb6cb46 --- /dev/null +++ b/src/actions/resolve_name.py @@ -0,0 +1,82 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from typing import TYPE_CHECKING + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + + +if TYPE_CHECKING: + from ..app import Asset +from ..helper import MsGraphHelper + + +class ResolveNameParams(Params): + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + name: str = Param( + description="Name or email to resolve", + required=True, + ) + + +class ResolvedContact(ActionOutput): + displayName: str | None = None + emailAddress: str | None = None + userPrincipalName: str | None = None + id: str | None = None + + +def render_resolve_name(output: list[ResolvedContact]) -> dict: + contacts = [] + for item in output: + contacts.append( + { + "id": item.id, + "user_principal_name": item.userPrincipalName, + "display_name": item.displayName, + "email_address": item.emailAddress, + } + ) + + results = [ + { + "data": bool(contacts), + "email": None, + "contacts": contacts, + } + ] + return {"results": results} + + +def resolve_name( + params: ResolveNameParams, soar: SOARClient, asset: "Asset" +) -> list[ResolvedContact]: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.email_address}/people" + api_params = {"$search": f'"{params.name}"', "$top": "50"} + + resp = helper.make_rest_call_helper(endpoint, params=api_params) + people = resp.get("value", []) + + results = [] + for person in people: + scored_emails = person.get("scoredEmailAddresses", []) + email_addr = scored_emails[0].get("address") if scored_emails else None + + results.append( + ResolvedContact( + displayName=person.get("displayName"), + emailAddress=email_addr, + userPrincipalName=person.get("userPrincipalName"), + id=person.get("id"), + ) + ) + + soar.set_message(f"Successfully resolved {len(results)} contacts") + return results diff --git a/src/actions/run_query.py b/src/actions/run_query.py new file mode 100644 index 0000000..1e5b3f8 --- /dev/null +++ b/src/actions/run_query.py @@ -0,0 +1,179 @@ +# Copyright (c) 2017-2026 Splunk Inc. + +import json +from typing import TYPE_CHECKING + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + + +if TYPE_CHECKING: + from ..app import Asset +from ..consts import MSGOFFICE365_PER_PAGE_COUNT, MSGOFFICE365_SELECT_PARAMETER_LIST +from ..helper import MsGraphHelper, serialize_complex_fields + + +class RunQueryParams(Params): + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + folder: str = Param( + description="Folder name/path or ID", + required=False, + default="Inbox", + ) + get_folder_id: bool = Param( + description="Retrieve folder ID from folder name/path", + required=False, + default=True, + ) + subject: str = Param( + description="Substring to search in subject", + required=False, + default="", + ) + sender: str = Param( + description="Sender email to search", + required=False, + default="", + ) + body: str = Param( + description="Substring to search in body", + required=False, + default="", + ) + internet_message_id: str = Param( + description="Internet Message ID to search", + required=False, + default="", + ) + limit: int = Param( + description="Maximum number of emails to return", + required=False, + default=100, + ) + search_well_known_folders: bool = Param( + description="Search in well-known folders", + required=False, + default=False, + ) + + +class EmailResult(ActionOutput): + id: str | None = None + subject: str | None = None + sender: str | None = None + receivedDateTime: str | None = None + bodyPreview: str | None = None + hasAttachments: bool | None = None + internetMessageId: str | None = None + parentFolderId: str | None = None + + +class RunQuerySummary(ActionOutput): + emails_matched: int = 0 + + +def _extract_sender_address(sender_json): + if not sender_json: + return None + try: + data = json.loads(sender_json) if isinstance(sender_json, str) else sender_json + return data.get("emailAddress", {}).get("address") + except (json.JSONDecodeError, AttributeError, ValueError): + return None + + +def render_run_query(output: list[EmailResult]) -> dict: + emails = [] + for item in output: + emails.append( + { + "id": item.id, + "subject": item.subject, + "sender_address": _extract_sender_address(item.sender), + "received_date_time": item.receivedDateTime, + "body_preview": item.bodyPreview, + "internet_message_id": item.internetMessageId, + } + ) + + results = [ + { + "data": bool(emails), + "param_email_address": None, + "param_folder": None, + "param_get_folder_id": None, + "param_subject": None, + "param_body": None, + "param_sender": None, + "param_limit": None, + "param_internet_message_id": None, + "emails": emails, + } + ] + return {"results": results} + + +def run_query( + params: RunQueryParams, soar: SOARClient, asset: "Asset" +) -> list[EmailResult]: + if params.limit is not None and params.limit <= 0: + raise ValueError("'limit' action parameter must be a positive integer") + + helper = MsGraphHelper(soar, asset) + helper.get_token() + + folder_id = params.folder + if params.get_folder_id and params.folder: + resolved_id = helper.get_folder_id(params.folder, params.email_address) + if resolved_id: + folder_id = resolved_id + + if params.search_well_known_folders: + endpoint = f"/users/{params.email_address}/messages" + else: + endpoint = f"/users/{params.email_address}/mailFolders/{folder_id}/messages" + + filters = [] + if params.subject: + filters.append(f"contains(subject, '{params.subject}')") + if params.sender: + filters.append(f"from/emailAddress/address eq '{params.sender}'") + if params.internet_message_id: + filters.append(f"internetMessageId eq '{params.internet_message_id}'") + + search = None + if params.body: + search = f'"{params.body}"' + + select_fields = ",".join(MSGOFFICE365_SELECT_PARAMETER_LIST) + api_params = {"$select": select_fields} + if filters: + api_params["$filter"] = " and ".join(filters) + if search: + api_params["$search"] = search + if params.limit: + api_params["$top"] = str(min(params.limit, MSGOFFICE365_PER_PAGE_COUNT)) + + emails = [] + next_link = None + while len(emails) < params.limit: + resp = helper.make_rest_call_helper( + endpoint, params=api_params, nextLink=next_link + ) + emails.extend(resp.get("value", [])) + + next_link = resp.get("@odata.nextLink") + if not next_link: + break + api_params = None + + emails = emails[: params.limit] + emails = [serialize_complex_fields(e, ["sender"]) for e in emails] + soar.set_message(f"Successfully retrieved {len(emails)} emails") + soar.set_summary(RunQuerySummary(emails_matched=len(emails))) + return [EmailResult(**e) for e in emails] diff --git a/src/actions/send_email.py b/src/actions/send_email.py new file mode 100644 index 0000000..5aba89f --- /dev/null +++ b/src/actions/send_email.py @@ -0,0 +1,85 @@ +# Copyright (c) 2017-2026 Splunk Inc. +import json + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class SendEmailParams(Params): + from_email: str = Param( + description="From email address", + required=True, + cef_types=["email"], + ) + to: str = Param( + description="To email addresses (comma-separated)", + required=True, + cef_types=["email"], + ) + cc: str = Param( + description="CC email addresses (comma-separated)", + required=False, + default="", + ) + bcc: str = Param( + description="BCC email addresses (comma-separated)", + required=False, + default="", + ) + subject: str = Param( + description="Email subject", + required=True, + ) + body: str = Param( + description="Email body", + required=True, + ) + body_is_html: bool = Param( + description="Is body HTML", + required=False, + default=False, + ) + + +class SendEmailOutput(ActionOutput): + message: str | None = None + + +def _parse_recipients(email_str: str) -> list: + if not email_str: + return [] + emails = [e.strip() for e in email_str.split(",") if e.strip()] + return [{"emailAddress": {"address": e}} for e in emails] + + +@app.action(description="Send an email", action_type="generic") +def send_email( + params: SendEmailParams, soar: SOARClient, asset: Asset +) -> SendEmailOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + message = { + "subject": params.subject, + "body": { + "contentType": "HTML" if params.body_is_html else "Text", + "content": params.body, + }, + "toRecipients": _parse_recipients(params.to), + } + + if params.cc: + message["ccRecipients"] = _parse_recipients(params.cc) + if params.bcc: + message["bccRecipients"] = _parse_recipients(params.bcc) + + endpoint = f"/users/{params.from_email}/sendMail" + body = {"message": message, "saveToSentItems": True} + helper.make_rest_call_helper(endpoint, method="post", data=json.dumps(body)) + + soar.set_message("Email sent successfully") + return SendEmailOutput(message="Email sent successfully") diff --git a/src/actions/unblock_sender.py b/src/actions/unblock_sender.py new file mode 100644 index 0000000..d4f3be2 --- /dev/null +++ b/src/actions/unblock_sender.py @@ -0,0 +1,58 @@ +# Copyright (c) 2017-2026 Splunk Inc. +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class UnblockSenderParams(Params): + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + sender: str = Param( + description="Email address of sender to unblock", + required=True, + cef_types=["email"], + ) + + +class UnblockSenderOutput(ActionOutput): + message: str | None = None + + +@app.action( + description="Remove a sender from the blocked senders list", action_type="correct" +) +def unblock_sender( + params: UnblockSenderParams, soar: SOARClient, asset: Asset +) -> UnblockSenderOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.email_address}/mailFolders/inbox/messageRules" + resp = helper.make_rest_call_helper(endpoint) + + rules = resp.get("value", []) + rule_id = None + for rule in rules: + display_name = rule.get("displayName", "") + if f"Block sender: {params.sender}" in display_name: + rule_id = rule.get("id") + break + + if not rule_id: + raise ValueError(f"No blocking rule found for sender: {params.sender}") + + delete_endpoint = ( + f"/users/{params.email_address}/mailFolders/inbox/messageRules/{rule_id}" + ) + helper.make_rest_call_helper(delete_endpoint, method="delete") + + soar.set_message(f"Successfully unblocked sender: {params.sender}") + return UnblockSenderOutput( + message=f"Successfully unblocked sender: {params.sender}" + ) diff --git a/src/actions/update_email.py b/src/actions/update_email.py new file mode 100644 index 0000000..9131566 --- /dev/null +++ b/src/actions/update_email.py @@ -0,0 +1,66 @@ +# Copyright (c) 2017-2026 Splunk Inc. +import json + +from soar_sdk.abstract import SOARClient +from soar_sdk.action_results import ActionOutput +from soar_sdk.params import Param, Params + +from ..app import Asset, app +from ..helper import MsGraphHelper + + +class UpdateEmailParams(Params): + id: str = Param( + description="Message ID to update", + required=True, + primary=True, + cef_types=["msgoffice365 message id"], + ) + email_address: str = Param( + description="User's email address (mailbox)", + required=True, + cef_types=["email"], + ) + category: str = Param( + description="Category to add to the email", + required=False, + default="", + ) + is_read: bool = Param( + description="Mark email as read", + required=False, + default=None, + ) + + +class UpdateEmailOutput(ActionOutput): + id: str | None = None + subject: str | None = None + isRead: bool | None = None + categories: str | None = None + + +@app.action(description="Update properties of an email", action_type="generic") +def update_email( + params: UpdateEmailParams, soar: SOARClient, asset: Asset +) -> UpdateEmailOutput: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + endpoint = f"/users/{params.email_address}/messages/{params.id}" + + body = {} + if params.category: + body["categories"] = [params.category] + if params.is_read is not None: + body["isRead"] = params.is_read + + if not body: + raise ValueError( + "At least one update parameter (category or is_read) must be provided" + ) + + resp = helper.make_rest_call_helper(endpoint, method="patch", data=json.dumps(body)) + + soar.set_message("Successfully updated email") + return UpdateEmailOutput(**resp) diff --git a/src/app.py b/src/app.py new file mode 100644 index 0000000..dd3efb0 --- /dev/null +++ b/src/app.py @@ -0,0 +1,1045 @@ +# 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. + +import base64 +import hashlib +import re +import time +from collections.abc import Generator, Iterator +from datetime import UTC, datetime +from email.utils import parseaddr, parsedate_to_datetime +from html import unescape + +from bs4 import BeautifulSoup +from soar_sdk.abstract import SOARClient +from soar_sdk.app import App +from soar_sdk.asset import AssetField, BaseAsset, FieldCategory +from soar_sdk.auth import ( + AuthorizationCodeFlow, + ClientCredentialsFlow, + OAuthConfig, + SOARAssetOAuthClient, +) +from soar_sdk.extras.email.email_data import EmailData, extract_email_data +from soar_sdk.extras.email.processor import ( + HASH_REGEX, + IP_REGEX, + URI_REGEX, +) +from soar_sdk.extras.email.utils import clean_url, is_ip +from soar_sdk.logging import getLogger +from soar_sdk.models.artifact import Artifact +from soar_sdk.models.container import Container +from soar_sdk.models.finding import ( + Finding, + FindingAttachment, + FindingEmail, + FindingEmailReporter, +) +from soar_sdk.params import OnESPollParams, OnPollParams +from soar_sdk.webhooks.models import WebhookRequest, WebhookResponse + +from .consts import ( + MSGOFFICE365_CONTAINER_DESCRIPTION, + MSGOFFICE365_DEFAULT_FOLDER, + MSGOFFICE365_DEFAULT_SCOPE, + MSGOFFICE365_ORDERBY_RECEIVED_DESC, + MSGOFFICE365_PER_PAGE_COUNT, + MSGOFFICE365_SELECT_PARAMETER_LIST, +) +from .helper import MsGraphHelper + + +logger = getLogger() + +APP_ID = "0a0a4087-10e8-4c96-9872-b740ff26d8bb" +APP_NAME = "MS Graph for Office 365" + +ADMIN_CONSENT_STATE_KEY = "admin_consent_granted" + + +def _extract_urls_domains( + body: str, extract_urls: bool, extract_domains: bool +) -> tuple[set[str], set[str]]: + """Extract URLs and domains from email body using SDK utilities.""" + urls: set[str] = set() + domains: set[str] = set() + + if not extract_urls and not extract_domains: + return urls, domains + + try: + soup = BeautifulSoup(body, "html.parser") + except Exception as e: + logger.debug(f"Error parsing HTML: {e}") + return urls, domains + + uris = [] + for link in soup.find_all(href=True): + uris.append(clean_url(link.get_text())) + if not link["href"].startswith("mailto:"): + uris.append(link["href"]) + + for src in soup.find_all(src=True): + uris.append(clean_url(src.get_text())) + uris.append(src["src"]) + + body_unescaped = unescape(body) + regex_uris = re.findall(URI_REGEX, body_unescaped) + uris.extend(clean_url(x) for x in regex_uris) + + for uri in uris: + if extract_urls and uri.startswith(("http://", "https://")): + urls.add(uri) + if extract_domains: + try: + from urllib.parse import urlparse + + parsed = urlparse(uri) + if parsed.netloc: + domains.add(parsed.netloc.split(":")[0]) + except Exception: + pass + + return urls, domains + + +def _extract_ips(body: str) -> set[str]: + """Extract IP addresses from email body using SDK utilities.""" + ips: set[str] = set() + for match in re.finditer(IP_REGEX, body): + ip_candidate = match.group(0).strip() + if is_ip(ip_candidate): + ips.add(ip_candidate) + return ips + + +def _extract_hashes(body: str) -> set[str]: + """Extract hashes from email body using SDK utilities.""" + return set(re.findall(HASH_REGEX, body)) + + +class Asset(BaseAsset): + # Connectivity fields + tenant: str = AssetField( + required=True, + description="Tenant ID (e.g. 1e309abf-db6c-XXXX-a1d2-XXXXXXXXXXXX)", + category=FieldCategory.CONNECTIVITY, + ) + client_id: str = AssetField( + required=True, + description="Application ID", + category=FieldCategory.CONNECTIVITY, + ) + auth_type: str = AssetField( + required=True, + description="Authentication type to use for connectivity", + default="Automatic", + value_list=["Automatic", "OAuth", "Certificate Based Authentication(CBA)"], + category=FieldCategory.CONNECTIVITY, + ) + client_secret: str = AssetField( + required=False, + description="Application Secret (required for OAuth)", + sensitive=True, + category=FieldCategory.CONNECTIVITY, + ) + certificate_thumbprint: str = AssetField( + required=False, + description="Certificate Thumbprint (required for CBA)", + sensitive=True, + category=FieldCategory.CONNECTIVITY, + ) + certificate_private_key: str = AssetField( + required=False, + description="Certificate Private Key (.PEM)", + sensitive=True, + category=FieldCategory.CONNECTIVITY, + ) + admin_access: bool = AssetField( + required=False, + description="Admin Access Required", + default=True, + category=FieldCategory.CONNECTIVITY, + ) + admin_consent: bool = AssetField( + required=False, + description="Admin Consent Already Provided (Required checked for CBA)", + default=False, + category=FieldCategory.CONNECTIVITY, + ) + scope: str = AssetField( + required=False, + description="Access Scope (for use with OAuth non-admin access; space-separated)", + default="https://graph.microsoft.com/Calendars.Read https://graph.microsoft.com/User.Read", + category=FieldCategory.CONNECTIVITY, + ) + retry_count: int = AssetField( + required=False, + description="Maximum attempts to retry the API call (Default: 3)", + default=3, + category=FieldCategory.CONNECTIVITY, + ) + retry_wait_time: int = AssetField( + required=False, + description="Delay in seconds between retries (Default: 60)", + default=60, + category=FieldCategory.CONNECTIVITY, + ) + + # Ingestion fields + email_address: str = AssetField( + required=False, + description="Email Address of the User (On Poll)", + ) + folder: str = AssetField( + required=False, + description="Mailbox folder name/folder path or the internal office365 folder ID to ingest (On Poll)", + ) + get_folder_id: bool = AssetField( + required=False, + description="Retrieve the folder ID for the provided folder name/folder path automatically", + default=True, + ) + first_run_max_emails: int = AssetField( + required=False, + description="Maximum emails for scheduled polling first time", + default=1000, + category=FieldCategory.INGEST, + ) + max_containers: int = AssetField( + required=False, + description="Maximum emails for scheduled polling", + default=100, + ) + extract_attachments: bool = AssetField( + required=False, + description="Extract Attachments", + default=False, + category=FieldCategory.INGEST, + ) + extract_urls: bool = AssetField( + required=False, + description="Extract URLs", + default=False, + category=FieldCategory.INGEST, + ) + extract_ips: bool = AssetField( + required=False, + description="Extract IPs", + default=False, + category=FieldCategory.INGEST, + ) + extract_domains: bool = AssetField( + required=False, + description="Extract Domain Names", + default=False, + category=FieldCategory.INGEST, + ) + extract_hashes: bool = AssetField( + required=False, + description="Extract Hashes", + default=False, + category=FieldCategory.INGEST, + ) + ingest_eml: bool = AssetField( + required=False, + description="Ingest EML file for the itemAttachment", + default=False, + category=FieldCategory.INGEST, + ) + ingest_manner: str = AssetField( + required=False, + description="How to Ingest", + default="oldest first", + value_list=["oldest first", "latest first"], + ) + extract_eml: bool = AssetField( + required=False, + description="Extract root (primary) email as Vault", + default=False, + ) + + +app = App( + name="MS Graph for Office 365", + app_type="email", + logo="logo_microsoftoffice365.svg", + logo_dark="logo_microsoftoffice365_dark.svg", + product_vendor="Microsoft", + product_name="Office 365 (MS Graph)", + publisher="Splunk", + appid=APP_ID, + fips_compliant=True, + asset_cls=Asset, +).enable_webhooks(default_requires_auth=False) + + +MS_GRAPH_AUTH_URL = "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize" +MS_GRAPH_TOKEN_URL = "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token" +MS_GRAPH_ADMIN_CONSENT_URL = "https://login.microsoftonline.com/{tenant}/adminconsent" + + +@app.test_connectivity() +def test_connectivity(soar: SOARClient, asset: Asset) -> None: + redirect_uri = app.get_webhook_url("result") + logger.info("Using OAuth Redirect URL as:") + logger.info(redirect_uri) + + auth_type = (asset.auth_type or "automatic").lower() + is_cba = auth_type == "certificate based authentication(cba)" or ( + auth_type == "automatic" and not asset.client_secret + ) + + if is_cba: + logger.info("Using Certificate Based Authentication...") + helper = MsGraphHelper(soar, asset) + helper.get_token() + + elif asset.admin_access: + asset_id = str(soar.get_asset_id()) + if not asset.admin_consent: + admin_consent_url = ( + f"{MS_GRAPH_ADMIN_CONSENT_URL.format(tenant=asset.tenant)}" + f"?client_id={asset.client_id}" + f"&redirect_uri={redirect_uri}" + f"&state={asset_id}" + ) + logger.info( + "Please connect to the following URL from a different tab to continue the connectivity process" + ) + logger.info(admin_consent_url) + + logger.info("Waiting for Admin Consent to complete") + poll_timeout = 300 + poll_interval = 3 + start_time = time.time() + + while time.time() - start_time < poll_timeout: + time.sleep(poll_interval) + if asset.auth_state.get(ADMIN_CONSENT_STATE_KEY): + logger.info("Admin consent received successfully") + break + else: + raise TimeoutError( + f"Admin consent timed out after {poll_timeout} seconds. " + "Please ensure you complete the consent process in your browser." + ) + + flow = ClientCredentialsFlow( + asset.auth_state, + client_id=asset.client_id, + client_secret=asset.client_secret, + token_endpoint=MS_GRAPH_TOKEN_URL.format(tenant=asset.tenant), + scope=[MSGOFFICE365_DEFAULT_SCOPE], + ) + flow.get_token() + logger.info("Successfully obtained token via client credentials") + + else: + if not asset.scope: + raise ValueError("Scope is required for non-admin access") + + scopes = asset.scope.split() + flow = AuthorizationCodeFlow( + asset.auth_state, + str(soar.get_asset_id()), + client_id=asset.client_id, + client_secret=asset.client_secret, + authorization_endpoint=MS_GRAPH_AUTH_URL.format(tenant=asset.tenant), + token_endpoint=MS_GRAPH_TOKEN_URL.format(tenant=asset.tenant), + redirect_uri=redirect_uri, + scope=scopes, + ) + + auth_url = flow.get_authorization_url() + logger.info( + "Please connect to the following URL from a different tab to continue the connectivity process" + ) + logger.info(auth_url) + flow.wait_for_authorization() + logger.info("Successfully obtained token via authorization code flow") + + helper = MsGraphHelper(soar, asset) + helper.get_token() + + params = {"$top": "1"} + if asset.admin_access or is_cba: + logger.info("Getting info about all users to verify token") + helper.make_rest_call_helper("/users", params=params) + else: + logger.info("Getting info about a single user to verify token") + helper.make_rest_call_helper("/me", params=params) + + soar.set_message("Test Connectivity Passed") + logger.info("Test Connectivity Passed") + + +@app.webhook("result") +def handle_oauth_result(request: WebhookRequest[Asset]) -> WebhookResponse: + query_params = {k: v[0] if v else "" for k, v in request.query.items()} + + if "error" in query_params: + reason = query_params.get("error_description", "Unknown error") + return WebhookResponse.text_response( + content=f"Authorization failed: {reason}", + status_code=400, + ) + + # Admin consent callback - Microsoft returns admin_consent=True instead of a code + if query_params.get("admin_consent", "").lower() == "true": + request.asset.auth_state[ADMIN_CONSENT_STATE_KEY] = True + return WebhookResponse.text_response( + content="Admin consent received. You can close this window.", + status_code=200, + ) + + # Authorization code callback + code = query_params.get("code") + if not code: + return WebhookResponse.text_response( + content="Missing authorization code", status_code=400 + ) + + oauth_client = SOARAssetOAuthClient( + OAuthConfig( + client_id=request.asset.client_id, + client_secret=request.asset.client_secret, + authorization_endpoint=MS_GRAPH_AUTH_URL.format( + tenant=request.asset.tenant + ), + token_endpoint=MS_GRAPH_TOKEN_URL.format(tenant=request.asset.tenant), + redirect_uri=app.get_webhook_url("result"), + scope=request.asset.scope.split() if request.asset.scope else [], + ), + request.asset.auth_state, + ) + oauth_client.set_authorization_code(code) + + return WebhookResponse.text_response( + content="Authorization successful! You can close this window.", + status_code=200, + ) + + +@app.on_poll() +def on_poll( + params: OnPollParams, soar: SOARClient, asset: Asset +) -> Iterator[Container | Artifact]: + helper = MsGraphHelper(soar, asset) + helper.get_token() + + state = asset.ingest_state + email_address = asset.email_address + if not email_address: + raise ValueError("Email address is required for polling") + + folder = asset.folder or MSGOFFICE365_DEFAULT_FOLDER + folder_id = folder + if asset.get_folder_id: + resolved_id = helper.get_folder_id(folder, email_address) + if resolved_id: + folder_id = resolved_id + + is_poll_now = params.container_count is not None + if is_poll_now: + max_emails = params.container_count if params.container_count > 0 else 100 + last_time = None + else: + is_first_run = state.get("first_run", True) + max_emails = ( + asset.first_run_max_emails if is_first_run else asset.max_containers + ) + last_time = state.get("last_time") + + endpoint = f"/users/{email_address}/mailFolders/{folder_id}/messages" + select_fields = ",".join(MSGOFFICE365_SELECT_PARAMETER_LIST) + api_params = { + "$select": select_fields, + "$top": str(min(max_emails, MSGOFFICE365_PER_PAGE_COUNT)), + "$orderby": MSGOFFICE365_ORDERBY_RECEIVED_DESC + if asset.ingest_manner == "latest first" + else "receivedDateTime asc", + } + + if last_time and not is_poll_now: + api_params["$filter"] = f"receivedDateTime gt {last_time}" + + emails_processed = 0 + latest_time = last_time + + while emails_processed < max_emails: + resp = helper.make_rest_call_helper(endpoint, params=api_params) + emails = resp.get("value", []) + + if not emails: + break + + for email_data in emails: + if emails_processed >= max_emails: + break + + email_time = email_data.get("receivedDateTime") + if email_time and (not latest_time or email_time > latest_time): + latest_time = email_time + + email_id = email_data.get("id") + container = Container( + name=email_data.get("subject") or email_id, + source_data_identifier=email_id, + description=MSGOFFICE365_CONTAINER_DESCRIPTION.format( + last_modified_time=email_data.get("lastModifiedDateTime") + ), + ) + yield container + + artifact = Artifact( + name="Email Artifact", + label="email", + cef={ + "messageId": email_id, + "subject": email_data.get("subject"), + "fromEmail": email_data.get("from", {}) + .get("emailAddress", {}) + .get("address"), + "receivedDateTime": email_data.get("receivedDateTime"), + "bodyPreview": email_data.get("bodyPreview"), + }, + cef_types={ + "messageId": ["msgoffice365 message id"], + "fromEmail": ["email"], + }, + ) + yield artifact + + body = email_data.get("body", {}).get("content", "") or email_data.get( + "bodyPreview", "" + ) + + if asset.extract_urls or asset.extract_domains: + urls, domains = _extract_urls_domains( + body, asset.extract_urls, asset.extract_domains + ) + for url in urls: + yield Artifact( + name="URL Artifact", + label="url", + cef={"requestURL": url}, + cef_types={"requestURL": ["url"]}, + ) + for domain in domains: + yield Artifact( + name="Domain Artifact", + label="domain", + cef={"destinationDnsDomain": domain}, + cef_types={"destinationDnsDomain": ["domain"]}, + ) + + if asset.extract_ips: + ips = _extract_ips(body) + for ip in ips: + yield Artifact( + name="IP Artifact", + label="ip", + cef={"destinationAddress": ip}, + cef_types={"destinationAddress": ["ip"]}, + ) + + if asset.extract_hashes: + hashes = _extract_hashes(body) + for file_hash in hashes: + yield Artifact( + name="Hash Artifact", + label="hash", + cef={"fileHash": file_hash}, + cef_types={"fileHash": ["hash"]}, + ) + + # extract_eml: Save the root email as EML file to vault + if asset.extract_eml: + try: + eml_content = helper.make_rest_call_helper( + f"/users/{email_address}/messages/{email_id}/$value", + download=True, + ) + if eml_content: + if isinstance(eml_content, str): + eml_content = eml_content.encode("utf-8") + file_hash = hashlib.sha256(eml_content).hexdigest() + subject = ( + email_data.get("subject") or f"email_message_{email_id}" + ) + file_name = f"{subject}.eml" + vault_info = soar.vault.add( + file_content=eml_content, + file_name=file_name, + ) + yield Artifact( + name="Vault Artifact", + label="email attachment", + cef={ + "vaultId": vault_info.vault_id + if hasattr(vault_info, "vault_id") + else str(vault_info), + "fileName": file_name, + "fileHashSha256": file_hash, + }, + cef_types={ + "vaultId": ["vault id"], + "fileName": ["file name"], + "fileHashSha256": ["hash", "sha256"], + }, + ) + except Exception as e: + logger.warning(f"Failed to extract root email as EML: {e}") + + if asset.extract_attachments and email_data.get("hasAttachments"): + try: + attachments_resp = helper.make_rest_call_helper( + f"/users/{email_address}/messages/{email_id}/attachments" + ) + for att in attachments_resp.get("value", []): + att_type = att.get("@odata.type") + + # Handle regular file attachments + if att_type == "#microsoft.graph.fileAttachment": + content_bytes = att.get("contentBytes") + if content_bytes: + try: + file_content = base64.b64decode(content_bytes) + file_hash = hashlib.sha256(file_content).hexdigest() + vault_info = soar.vault.add( + file_content=file_content, + file_name=att.get("name", "attachment"), + ) + yield Artifact( + name="Vault Artifact", + label="attachment", + cef={ + "vaultId": vault_info.vault_id + if hasattr(vault_info, "vault_id") + else str(vault_info), + "fileName": att.get("name"), + "fileSize": att.get("size"), + "fileHashSha256": file_hash, + }, + cef_types={ + "vaultId": ["vault id"], + "fileName": ["file name"], + "fileHashSha256": ["hash", "sha256"], + }, + ) + except Exception as e: + logger.warning( + f"Failed to save attachment to vault: {e}" + ) + + # Handle itemAttachment (embedded emails) - ingest_eml feature + elif ( + att_type == "#microsoft.graph.itemAttachment" + and asset.ingest_eml + ): + att_id = att.get("id") + try: + eml_content = helper.make_rest_call_helper( + f"/users/{email_address}/messages/{email_id}/attachments/{att_id}/$value", + download=True, + ) + if eml_content: + if isinstance(eml_content, str): + eml_content = eml_content.encode("utf-8") + file_hash = hashlib.sha256(eml_content).hexdigest() + att_name = att.get("name", "embedded_email") + file_name = f"{att_name}.eml" + vault_info = soar.vault.add( + file_content=eml_content, + file_name=file_name, + ) + yield Artifact( + name="Vault Artifact", + label="attachment", + cef={ + "vaultId": vault_info.vault_id + if hasattr(vault_info, "vault_id") + else str(vault_info), + "fileName": file_name, + "fileSize": att.get("size"), + "lastModified": att.get( + "lastModifiedDateTime" + ), + "mimeType": att.get("contentType"), + "fileHashSha256": file_hash, + }, + cef_types={ + "vaultId": ["vault id"], + "fileName": ["file name"], + "fileHashSha256": ["hash", "sha256"], + }, + ) + except Exception as e: + logger.warning( + f"Failed to save item attachment to vault: {e}" + ) + + except Exception as e: + logger.warning( + f"Failed to fetch attachments for email {email_id}: {e}" + ) + + emails_processed += 1 + + next_link = resp.get("@odata.nextLink") + if not next_link or emails_processed >= max_emails: + break + api_params = None + resp = helper.make_rest_call_helper(endpoint, nextLink=next_link) + + if not is_poll_now and latest_time: + state["last_time"] = latest_time + state["first_run"] = False + + +def _format_date_fallback(date_header: str | None) -> str: + """Parse an email Date header into 'YYYY-MM-DD HH:mm UTC' format.""" + if date_header: + try: + dt = parsedate_to_datetime(date_header) + utc_dt = dt.astimezone(UTC) + return utc_dt.strftime("%Y-%m-%d %H:%M UTC") + except (ValueError, TypeError): + pass + return datetime.now(UTC).strftime("%Y-%m-%d %H:%M UTC") + + +def _extract_address(header_value: str | None) -> str | None: + """Extract a single clean email address from a header value.""" + if not header_value: + return None + _, addr = parseaddr(header_value) + return addr or None + + +def _extract_inner_email( + outer_parsed: EmailData, email_id: str | None +) -> tuple[EmailData, FindingEmailReporter] | None: + """If the email has an .eml/.msg attachment, extract it and build reporter info.""" + for att in outer_parsed.attachments: + if not att.content: + continue + lower_name = att.filename.lower() + if not (lower_name.endswith(".eml") or lower_name.endswith(".msg")): + continue + + inner_parsed = extract_email_data( + att.content, email_id, include_attachment_content=True + ) + + outer_headers = outer_parsed.headers + body_text = outer_parsed.body.plain_text or outer_parsed.body.html or "" + reporter = FindingEmailReporter( + from_=_extract_address(outer_headers.from_address), + to=_extract_address(outer_headers.to), + cc=_extract_address(outer_headers.cc), + bcc=_extract_address(outer_headers.bcc), + subject=outer_headers.subject, + message_id=outer_headers.message_id, + id=email_id, + body=body_text, + date=outer_headers.date, + ) + return inner_parsed, reporter + + return None + + +@app.on_es_poll() +def on_es_poll( + params: OnESPollParams, soar: SOARClient, asset: Asset +) -> Generator[Finding, int | None]: + """Poll for new emails and create ES findings for each email.""" + helper = MsGraphHelper(soar, asset) + helper.get_token() + + state = asset.ingest_state + email_address = asset.email_address + if not email_address: + raise ValueError("Email address is required for ES polling") + + folder = asset.folder or MSGOFFICE365_DEFAULT_FOLDER + folder_id = folder + if asset.get_folder_id: + resolved_id = helper.get_folder_id(folder, email_address) + if resolved_id: + folder_id = resolved_id + + max_emails = asset.max_containers + last_time = state.get("es_last_time") + boundary_ids = set(state.get("es_boundary_ids", [])) + + endpoint = f"/users/{email_address}/mailFolders/{folder_id}/messages" + select_fields = ",".join(MSGOFFICE365_SELECT_PARAMETER_LIST) + api_params = { + "$select": select_fields, + "$top": str(min(max_emails, MSGOFFICE365_PER_PAGE_COUNT)), + "$orderby": MSGOFFICE365_ORDERBY_RECEIVED_DESC + if asset.ingest_manner == "latest first" + else "receivedDateTime asc", + } + + if last_time: + api_params["$filter"] = f"receivedDateTime ge {last_time}" + + emails_processed = 0 + latest_time = last_time + new_boundary_ids: set[str] = set() + + while emails_processed < max_emails: + resp = helper.make_rest_call_helper(endpoint, params=api_params) + emails = resp.get("value", []) + + if not emails: + break + + for email_data in emails: + if emails_processed >= max_emails: + break + + email_id = email_data.get("id") + email_time = email_data.get("receivedDateTime") + + if email_time == last_time and email_id in boundary_ids: + continue + + if email_time and (not latest_time or email_time > latest_time): + latest_time = email_time + new_boundary_ids = set() + if email_time == latest_time: + new_boundary_ids.add(email_id) + subject = email_data.get("subject") or email_id + + finding_email = None + attachments: list[FindingAttachment] = [] + rule_title = subject[:100] if subject else f"Email ID: {email_id}" + + try: + eml_content = helper.make_rest_call_helper( + f"/users/{email_address}/messages/{email_id}/$value", + download=True, + ) + if eml_content: + raw_eml = ( + eml_content.encode("utf-8") + if isinstance(eml_content, str) + else eml_content + ) + eml_str = ( + eml_content + if isinstance(eml_content, str) + else eml_content.decode("utf-8", errors="replace") + ) + attachments.append( + FindingAttachment( + file_name=f"{subject[:50]}.eml", + data=raw_eml, + is_raw_email=True, + ) + ) + + try: + parsed = extract_email_data( + eml_str, email_id, include_attachment_content=True + ) + + sender = ( + email_data.get("from", {}) + .get("emailAddress", {}) + .get("address", "") + ) + + reporter = None + inner = _extract_inner_email(parsed, email_id) + if inner is not None: + parsed, reporter = inner + outer_attachments = attachments + attachments = [ + FindingAttachment( + file_name=f"{parsed.headers.subject or subject[:50]}.eml", + data=parsed.raw_email.encode("utf-8") + if isinstance(parsed.raw_email, str) + else parsed.raw_email, + is_raw_email=True, + ) + ] + if asset.extract_eml: + attachments.extend(outer_attachments) + + original_sender = ( + _extract_address(parsed.headers.from_address) or "" + ) + inner_subject = parsed.headers.subject + if inner_subject: + rule_title = f"{sender} reported email from {original_sender} - {inner_subject}" + else: + date_str = _format_date_fallback(parsed.headers.date) + rule_title = f"{sender} reported email from {original_sender} - No subject ({date_str})" + else: + outer_subject = email_data.get("subject") + if outer_subject: + rule_title = ( + f"{sender} reported email - {outer_subject}" + ) + else: + date_str = _format_date_fallback(parsed.headers.date) + rule_title = ( + f"{sender} reported email - No subject ({date_str})" + ) + + body_text = parsed.body.plain_text or parsed.body.html or "" + email_headers = { + k: v for k, v in parsed.to_dict()["headers"].items() if v + } + finding_email = FindingEmail( + headers=email_headers or None, + body=body_text or None, + urls=parsed.urls or None, + reporter=reporter, + ) + for att in parsed.attachments: + if att.content: + attachments.append( + FindingAttachment( + file_name=att.filename, + data=att.content, + is_raw_email=False, + ) + ) + except Exception as e: + logger.warning(f"Failed to parse email content: {e}") + except Exception as e: + logger.warning(f"Failed to fetch email EML: {e}") + + emails_processed += 1 + + if latest_time: + state["es_last_time"] = latest_time + state["es_first_run"] = False + state["es_boundary_ids"] = list(new_boundary_ids) + + yield Finding( + rule_title=rule_title[:100], + email=finding_email, + attachments=attachments if attachments else None, + ) + + next_link = resp.get("@odata.nextLink") + if not next_link or emails_processed >= max_emails: + break + api_params = None + helper.make_rest_call_helper(endpoint, nextLink=next_link) + + logger.info(f"Processed {emails_processed} emails for ES findings") + + +# Import action modules to register them with the app +from .actions import ( # noqa: F401 + block_sender, + copy_email, + create_folder, + delete_email, + delete_event, + generate_token, + get_email_properties, + get_folder_id, + get_mailbox_messages, + list_folders, + list_group_members, + list_groups, + list_users, + move_email, + oof_check, + report_message, + send_email, + unblock_sender, + update_email, +) + +# Register actions that use custom views +from .actions.get_email import get_email, render_get_email + + +app.register_action( + get_email, + description="Get an email from the server", + action_type="investigate", + view_handler=render_get_email, + view_template="office365_get_email.html", +) + +from .actions.list_events import list_events, render_list_events + + +app.register_action( + list_events, + description="List events from user or group calendar", + action_type="investigate", + view_handler=render_list_events, + view_template="office365_list_events.html", +) + +from .actions.get_rule import get_rule, render_get_rule + + +app.register_action( + get_rule, + description="Get the properties and relationships of a messageRule object", + action_type="investigate", + view_handler=render_get_rule, + view_template="office365_get_rule.html", +) + +from .actions.list_rules import list_rules, render_list_rules + + +app.register_action( + list_rules, + description="Get all the messageRule objects defined for the user's inbox", + action_type="investigate", + view_handler=render_list_rules, + view_template="office365_list_rules.html", +) + +from .actions.resolve_name import render_resolve_name, resolve_name + + +app.register_action( + resolve_name, + description="Resolve a name to email addresses", + action_type="investigate", + view_handler=render_resolve_name, + view_template="office365_resolve_name.html", +) + +from .actions.run_query import render_run_query, run_query + + +app.register_action( + run_query, + description="Search emails in a mailbox", + action_type="investigate", + view_handler=render_run_query, + view_template="office365_run_query.html", +) + + +if __name__ == "__main__": + app.cli() diff --git a/office365_consts.py b/src/consts.py similarity index 52% rename from office365_consts.py rename to src/consts.py index 45d4779..23cb40e 100644 --- a/office365_consts.py +++ b/src/consts.py @@ -1,6 +1,4 @@ -# 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. @@ -12,17 +10,20 @@ # the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, # either express or implied. See the License for the specific language governing permissions # and limitations under the License. + TC_STATUS_SLEEP = 2 MSGOFFICE365_PER_PAGE_COUNT = 999 -MSGOFFICE365_UPLOAD_SESSION_CUTOFF = 3145728 # 3MB -MSGOFFICE365_UPLOAD_LARGE_FILE_CUTOFF = 52428800 # 52MB -SPLUNK_SOAR_SYS_INFO_URL = "{url}rest/system_info" -SPLUNK_SOAR_ASSET_INFO_URL = "{url}rest/asset/{asset_id}" -SPLUNK_SOAR_CONTAINER_INFO_URL = "{url}rest/container/{container_id}" +MSGOFFICE365_UPLOAD_SESSION_CUTOFF = 3145728 +MSGOFFICE365_UPLOAD_LARGE_FILE_CUTOFF = 52428800 O365_TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ" -MSGOFFICE365_RUN_CONNECTIVITY_MSG = ( - "Please run test connectivity first to complete authorization flow and generate a token that the app can use to make calls to the server " -) + +SERVER_TOKEN_URL = "https://login.microsoftonline.com/{0}/oauth2/v2.0/token" +MSGOFFICE365_AUTHORITY_URL = "https://login.microsoftonline.com/{tenant}" +MSGRAPH_API_URL = "https://graph.microsoft.com" +MAX_END_OFFSET_VAL = 2147483646 +MSGOFFICE365_DEFAULT_SCOPE = "https://graph.microsoft.com/.default" + +MSGOFFICE365_RUN_CONNECTIVITY_MSG = "Please run test connectivity first to complete authorization flow and generate a token" MSGOFFICE365_WELL_KNOWN_FOLDERS_FILTER = [ "archive", "clutter", @@ -42,39 +43,32 @@ "serverfailures", "syncissues", ] -MSGOFFICE365_STATE_FILE_CORRUPT_ERROR = ( - "Error occurred while loading the state file. Resetting the state file with the default format. Please test the connectivity." -) - -MSGOFFICE365_AUTHORIZE_TROUBLESHOOT_MSG = ( - "If authorization URL fails to communicate with your " - "Splunk SOAR instance, check whether you have: " - " 1. Specified the Web Redirect URL of your App -- The Redirect URL should be /result . " - " 2. Configured the base URL of your Splunk SOAR Instance at Administration -> Company Settings -> Info" -) +MSGOFFICE365_STATE_FILE_CORRUPT_ERROR = "Error occurred while loading the state file. Resetting the state file with the default format." MSGOFFICE365_INVALID_PERMISSION_ERROR = ( - "Error occurred while saving the newly generated access token (in place of the expired token) in the state file." -) -MSGOFFICE365_INVALID_PERMISSION_ERROR += " Please check the owner, owner group, and the permissions of the state file. The Splunk SOAR " -MSGOFFICE365_INVALID_PERMISSION_ERROR += ( - "user should have the correct access rights and ownership for the corresponding state file (refer to readme file for more information)." + "Error occurred while saving the newly generated access token in the state file." ) MSGOFFICE365_NO_DATA_FOUND = "No data found" MSGOFFICE365_DUPLICATE_CONTAINER_FOUND_MSG = "duplicate container found" -MSGOFFICE365_ERROR_EMPTY_RESPONSE = "Status Code {code}. Empty response and no information in the header." +MSGOFFICE365_ERROR_EMPTY_RESPONSE = ( + "Status Code {code}. Empty response and no information in the header." +) MSGOFFICE365_CBA_AUTH_ERROR = "Certificate Based Authentication requires both Certificate Thumbprint and Certificate Private Key" MSGOFFICE365_OAUTH_AUTH_ERROR = "OAuth Authentication requires Client Secret" -MSGOFFICE365_AUTOMATIC_AUTH_ERROR = ( - "Automatic Authentication requires either Client Secret or combination of Certificate Thumbprint and Certificate Private Key" +MSGOFFICE365_AUTOMATIC_AUTH_ERROR = "Automatic Authentication requires either Client Secret or combination of Certificate Thumbprint and Certificate Private Key" +MSGOFFICE365_CBA_ADMIN_CONSENT_ERROR = ( + "Certificate Based Authorization requires Admin Consent to be Provided" +) +MSGOFFICE365_CBA_KEY_ERROR = ( + "Error occurred while parsing the private key, is it in .PEM format?" ) -MSGOFFICE365_CBA_ADMIN_CONSENT_ERROR = "Certificate Based Authorization requires Admin Consent to be Provided" -MSGOFFICE365_CBA_KEY_ERROR = "Error occurred while parsing the private key, is it in .PEM format?" MSGOFFICE365_NON_ADMIN_SCOPE_ERROR = "Please provide scope for non-admin access in the asset configuration for OAuth authentication" -MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT = 30 # in seconds +MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT = 30 MSGOFFICE365_DEFAULT_NUMBER_OF_RETRIES = 3 -MSGOFFICE365_DEFAULT_RETRY_WAIT_TIME = 60 # in seconds -MSGOFFICE365_CONTAINER_DESCRIPTION = "Email ingested using MS Graph API - {last_modified_time}" +MSGOFFICE365_DEFAULT_RETRY_WAIT_TIME = 60 +MSGOFFICE365_CONTAINER_DESCRIPTION = ( + "Email ingested using MS Graph API - {last_modified_time}" +) MSGOFFICE365_HTTP_401_STATUS_CODE = "401" MSGOFFICE365_INVALID_CLIENT_ID_ERROR_CODE = "AADSTS700016" MSGOFFICE365_INVALID_TENANT_ID_FORMAT_ERROR_CODE = "AADSTS900023" @@ -86,13 +80,13 @@ MSGOFFICE365_INVALID_TENANT_ID_NOT_FOUND_ERROR_CODE, ] -# Constants relating to '_get_error_message_from_exception' - MSGOFFICE365_ERROR_MSG_UNAVAILABLE = "Error msg unavailable. Please check the asset configuration and|or action parameters" - -# Constants relating to 'validate_integer' -MSGOFFICE365_VALID_INT_MSG = "Please provide a valid integer value in the {param} parameter" -MSGOFFICE365_NON_NEG_NON_ZERO_INT_MSG = "Please provide a valid non-zero positive integer value in the {param} parameter" +MSGOFFICE365_VALID_INT_MSG = ( + "Please provide a valid integer value in the {param} parameter" +) +MSGOFFICE365_NON_NEG_NON_ZERO_INT_MSG = ( + "Please provide a valid non-zero positive integer value in the {param} parameter" +) MSGOFFICE365_AUTH_FAILURE_MSG = [ "token is invalid", "Access token has expired", @@ -102,12 +96,9 @@ "InvalidAuthenticationToken", "Lifetime validation failed, the token is expired.", ] -MSGOFFICE365_NON_NEG_INT_MSG = "Please provide a valid non-negative integer value in the {param} parameter" -MSGOFFICE365_ENCRYPTION_ERROR = "Error occurred while encrypting the state file" -MSGOFFICE365_DECRYPTION_ERROR = "Error occurred while decrypting the state file" -MSGOFFICE365_UNEXPECTED_ACCESS_TOKEN_ERROR = "Found unexpected value of access token. Please run the test connectivity to generate a new token" -MSGOFFICE365_INVALID_EMAIL = "Please provide a valid email-address in the 'identificator' parameter" -MSGOFFICE365_INVALID_METHOD = "Please provide a valid method in the 'method' parameter" +MSGOFFICE365_NON_NEG_INT_MSG = ( + "Please provide a valid non-negative integer value in the {param} parameter" +) MSGOFFICE365_SELECT_PARAMETER_LIST = [ "bccRecipients", @@ -143,10 +134,13 @@ "internetMessageId", ] -MSGOFFICE365_AUTH_TYPES = {"Automatic": "auto", "OAuth": "oauth", "Certificate Based Authentication(CBA)": "cba"} +MSGOFFICE365_AUTH_TYPES = { + "Automatic": "auto", + "OAuth": "oauth", + "Certificate Based Authentication(CBA)": "cba", +} MSGOFFICE365_AUTH_AUTOMATIC = "Automatic" - MSGOFFICE365_DEFAULT_FOLDER = "Inbox" MSGOFFICE365_DEFAULT_LIMIT = 100 MSGOFFICE365_ORDERBY_RECEIVED_DESC = "receivedDateTime desc" diff --git a/src/helper.py b/src/helper.py new file mode 100644 index 0000000..eeab56c --- /dev/null +++ b/src/helper.py @@ -0,0 +1,277 @@ +# 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. + +import json +import time + +import requests +from soar_sdk.abstract import SOARClient +from soar_sdk.logging import getLogger + +from .consts import ( + MSGOFFICE365_AUTH_FAILURE_MSG, + MSGOFFICE365_AUTH_TYPES, + MSGOFFICE365_AUTHORITY_URL, + MSGOFFICE365_CBA_KEY_ERROR, + MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT, + MSGOFFICE365_DEFAULT_RETRY_WAIT_TIME, + MSGOFFICE365_DEFAULT_SCOPE, + MSGOFFICE365_WELL_KNOWN_FOLDERS_FILTER, + MSGRAPH_API_URL, + SERVER_TOKEN_URL, +) + + +logger = getLogger() + + +class MsGraphHelper: + def __init__(self, soar: SOARClient, asset): + self.soar = soar + self.asset = asset + self._access_token = None + self._refresh_token = None + self._auth_type = MSGOFFICE365_AUTH_TYPES.get(asset.auth_type, "auto") + self._number_of_retries = asset.retry_count or 3 + self._retry_wait_time = ( + asset.retry_wait_time or MSGOFFICE365_DEFAULT_RETRY_WAIT_TIME + ) + + def _get_auth_state(self) -> dict: + return dict(self.asset.auth_state.get_all()) + + def _save_auth_state(self, state: dict): + self.asset.auth_state.put_all(state) + + def _generate_cba_access_token(self): + import msal # Lazy import due to BOM issue in msal package + + logger.info("Generating token using Certificate Based Authentication...") + authority = MSGOFFICE365_AUTHORITY_URL.format(tenant=self.asset.tenant) + try: + app_client = msal.ConfidentialClientApplication( + client_id=self.asset.client_id, + authority=authority, + client_credential={ + "thumbprint": self.asset.certificate_thumbprint, + "private_key": self.asset.certificate_private_key, + }, + ) + except ValueError as e: + if "private_key" in str(e).lower(): + raise Exception(MSGOFFICE365_CBA_KEY_ERROR) from None + raise + + res_json = app_client.acquire_token_for_client( + scopes=[MSGOFFICE365_DEFAULT_SCOPE] + ) + if error := res_json.get("error"): + error_message = f"{error}: {res_json.get('error_description')}" + raise Exception(error_message) + return res_json + + def _generate_oauth_access_token(self): + logger.info("Generating token using OAuth Authentication...") + req_url = SERVER_TOKEN_URL.format(self.asset.tenant) + headers = {"Content-Type": "application/x-www-form-urlencoded"} + state = self._get_auth_state() + + data = { + "client_id": self.asset.client_id, + "client_secret": self.asset.client_secret, + "grant_type": "client_credentials", + } + + if not self.asset.admin_access: + data["scope"] = "offline_access " + (self.asset.scope or "") + if state.get("code"): + data["redirect_uri"] = state.get("redirect_uri") + data["code"] = state.get("code") + data["grant_type"] = "authorization_code" + elif self._refresh_token: + data["refresh_token"] = self._refresh_token + data["grant_type"] = "refresh_token" + else: + data["scope"] = MSGOFFICE365_DEFAULT_SCOPE + + resp = requests.post( + req_url, + headers=headers, + data=data, + timeout=MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT, + ) + resp.raise_for_status() + return resp.json() + + def get_token(self): + state = self._get_auth_state() + if self.asset.admin_access: + self._access_token = state.get("admin_auth", {}).get("access_token") + else: + self._access_token = state.get("non_admin_auth", {}).get("access_token") + self._refresh_token = state.get("non_admin_auth", {}).get("refresh_token") + + if self._access_token: + return + + if self._auth_type == "cba" or not self.asset.client_secret: + resp_json = self._generate_cba_access_token() + state["auth_type"] = "cba" + else: + resp_json = self._generate_oauth_access_token() + state["auth_type"] = "oauth" + + if self.asset.admin_access: + if self.asset.admin_consent: + state["admin_consent"] = True + state["admin_auth"] = resp_json + else: + state["non_admin_auth"] = resp_json + + self._access_token = resp_json.get("access_token") + self._refresh_token = resp_json.get("refresh_token") + self._save_auth_state(state) + + def _make_rest_call( + self, url, method="get", headers=None, params=None, data=None, download=False + ): + if headers is None: + headers = {} + headers.update( + { + "Authorization": f"Bearer {self._access_token}", + "Accept": "application/json", + "Content-Type": "application/json", + } + ) + + request_func = getattr(requests, method) + for _ in range(self._number_of_retries): + resp = request_func( + url, + headers=headers, + params=params, + data=data, + timeout=MSGOFFICE365_DEFAULT_REQUEST_TIMEOUT, + ) + if resp.status_code == 429: + retry_after = int( + resp.headers.get("Retry-After", self._retry_wait_time) + ) + time.sleep(min(retry_after, 60)) + continue + if resp.status_code != 502: + break + time.sleep(self._retry_wait_time) + + if download: + if 200 <= resp.status_code < 399: + return resp.text + raise Exception(f"Error downloading: {resp.status_code}") + + if resp.status_code == 204: + return {} + + if not resp.text: + if 200 <= resp.status_code < 399: + return {} + raise Exception(f"Empty response with status {resp.status_code}") + + if "json" in resp.headers.get("Content-Type", ""): + resp_json = resp.json() + if 200 <= resp.status_code < 399: + return resp_json + error = resp_json.get("error", {}) + error_msg = ( + error.get("message", resp.text) + if isinstance(error, dict) + else str(error) + ) + raise Exception(f"API Error {resp.status_code}: {error_msg}") + + if resp.status_code >= 400: + raise Exception(f"Error {resp.status_code}: {resp.text[:500]}") + return {} + + def make_rest_call_helper( + self, + endpoint, + method="get", + params=None, + data=None, + nextLink=None, + download=False, + beta=False, + ): + if nextLink: + url = nextLink + else: + api_version = "beta" if beta else "v1.0" + url = f"{MSGRAPH_API_URL}/{api_version}{endpoint}" + + try: + return self._make_rest_call( + url, method=method, params=params, data=data, download=download + ) + except Exception as e: + error_msg = str(e) + if any(msg in error_msg for msg in MSGOFFICE365_AUTH_FAILURE_MSG): + logger.info("Token expired, refreshing...") + self._access_token = None + state = self._get_auth_state() + auth_key = ( + "admin_auth" if self.asset.admin_access else "non_admin_auth" + ) # pragma: allowlist secret + state.pop(auth_key, None) + self._save_auth_state(state) + self.get_token() + return self._make_rest_call( + url, method=method, params=params, data=data, download=download + ) + raise + + def get_folder_id(self, folder_name: str, email_address: str) -> str | None: + if not folder_name: + return None + + if folder_name.lower() in MSGOFFICE365_WELL_KNOWN_FOLDERS_FILTER: + return folder_name.lower() + + folders = folder_name.split("/") + parent_folder_id = None + + for folder in folders: + if not folder: + continue + if parent_folder_id: + endpoint = f"/users/{email_address}/mailFolders/{parent_folder_id}/childFolders" + else: + endpoint = f"/users/{email_address}/mailFolders" + + params = {"$filter": f"displayName eq '{folder}'"} + resp = self.make_rest_call_helper(endpoint, params=params) + value = resp.get("value", []) + if not value: + return None + parent_folder_id = value[0].get("id") + + return parent_folder_id + + +def serialize_complex_fields(resp: dict, fields: list[str]) -> dict: + """Serialize complex fields (dict/list) to JSON strings for ActionOutput.""" + for field in fields: + if field in resp and resp[field] is not None: + if isinstance(resp[field], (dict, list)): + resp[field] = json.dumps(resp[field]) + return resp diff --git a/templates/office365_get_email.html b/templates/office365_get_email.html new file mode 100644 index 0000000..02ccf34 --- /dev/null +++ b/templates/office365_get_email.html @@ -0,0 +1,229 @@ + +{% extends 'base/logo_header.html' %} +{% block widget_content %} + +
+ {% for result in results %} + {% if not result.data %} +

No data found

+ {% else %} +
+

Action Parameters

+ + + + + + + + + + {% if result.param_download_attachments %} + + + + + {% endif %} + {% if result.param_get_headers %} + + + + + {% endif %} +
Message ID{{ result.param_id }}
Email Address{{ result.param_email_address }}
Download attachments to vault{{ result.param_download_attachments }}
Extract email headers{{ result.param_get_headers }}
+
+
+

Data

+ + + + + + + + + + + + + + {% for email in result.emails %} + + + + + + + + + + {% endfor %} + +
Message IDSubjectBody PreviewFromToHas Attachments?Internet Message ID
{{ email.id }}{{ email.subject }}{{ email.body_preview }}{{ email.from_address or "None" }} + {% if email.to_addresses %} + {% for addr in email.to_addresses %} + {{ addr }} + {% if not loop.last %}
{% endif %} + {% endfor %} + {% else %} + None + {% endif %} +
{{ email.has_attachments }}{{ email.internet_message_id }}
+
+ {% if result.has_attachments and result.attachment_data %} +
+

Attachments

+ {% if result.attachment_data.file_attachment %} + + + + + + + + + + + {% for file in result.attachment_data.file_attachment %} + + + + + + + {% endfor %} + +
Attachment NameVault IDAttachment TypeContent Type
{{ file.name }}{{ file.vaultId }}{{ file.attachmentType }}{{ file.contentType }}
+ {% endif %} + {% if result.attachment_data.item_attachment %} + + + + + + + + + + + + {% for item in result.attachment_data.item_attachment %} + + + + + + + + {% endfor %} + +
Attachment NameVault IDAttachment TypeItem Attachment TypeContent Type
{{ item.name }}{{ item.vaultId }}{{ item.attachmentType }}{{ item.itemType }}{{ item.contentType }}
+ {% endif %} + {% if result.attachment_data.reference_attachment %} + + + + + + + + + + {% for ref in result.attachment_data.reference_attachment %} + + + + + + {% endfor %} + +
Attachment NameAttachment TypeContent Type
{{ ref.name }}{{ ref.attachmentType }}{{ ref.contentType }}
+ {% endif %} + {% if result.attachment_data.other_attachment %} + + + + + + + + + + {% for att in result.attachment_data.other_attachment %} + + + + + + {% endfor %} + +
Attachment NameAttachment TypeContent Type
{{ att.name }}{{ att.attachmentType }}{{ att.contentType }}
+ {% endif %} +
+ {% endif %} + {% endif %} + {% endfor %} +
+{% endblock %} diff --git a/templates/office365_get_rule.html b/templates/office365_get_rule.html new file mode 100644 index 0000000..be071f6 --- /dev/null +++ b/templates/office365_get_rule.html @@ -0,0 +1,124 @@ + +{% extends 'base/logo_header.html' %} +{% block widget_content %} + +
+ {% for result in results %} + {% if not result.data %} +

No data found

+ {% else %} +
+

Action Parameters

+ + + + + + + + + +
User ID{{ result.user_id }}
Rule ID{{ result.rule_id }}
+
+
+

Data

+ + + + + + + + + + {% for rule in result.rules %} + + + + + + {% endfor %} + +
Rule Display NameRule ActionsRule Conditions
{{ rule.display_name }} + + {% for key, value in rule.action_items.items() %} + + + + + {% endfor %} +
{{ key }}{{ value }}
+
+ + {% for key, value in rule.condition_items.items() %} + + + + + {% endfor %} +
{{ key }}{{ value }}
+
+
+ {% endif %} + {% endfor %} +
+{% endblock %} diff --git a/templates/office365_list_events.html b/templates/office365_list_events.html new file mode 100644 index 0000000..0dd9722 --- /dev/null +++ b/templates/office365_list_events.html @@ -0,0 +1,125 @@ + +{% extends 'base/logo_header.html' %} +{% block widget_content %} + +
+ {% for result in results %} + {% if not result.data %} +

No data found

+ {% else %} +
+

Info

+ + {% if result.user_id %} + + + + + {% endif %} + {% if result.group_id %} + + + + + {% endif %} + {% if result.limit %} + + + + + {% endif %} + {% if result.filter %} + + + + + {% endif %} +
User ID{{ result.user_id }}
Group ID{{ result.group_id }}
Limit{{ result.limit }}
Filter{{ result.filter }}
+
+
+

Data

+ + + + + + + + + + + + {% for event in result.events %} + + + + + + + + {% endfor %} + +
Event IDSubjectStart TimeEnd TimeAttendee(s)
{{ event.id or "None" }}{{ event.subject }}{{ event.start_time }}{{ event.end_time }}{{ event.attendee_list }}
+
+ {% endif %} + {% endfor %} +
+{% endblock %} diff --git a/templates/office365_list_rules.html b/templates/office365_list_rules.html new file mode 100644 index 0000000..91d4976 --- /dev/null +++ b/templates/office365_list_rules.html @@ -0,0 +1,80 @@ + +{% extends 'base/logo_header.html' %} +{% block widget_content %} + +
+ {% for result in results %} + {% if not result.data %} +

No data found

+ {% else %} +
+

Rules List

+ + + + + + + + + + + {% for rule in result.rules %} + + + + + + + {% endfor %} + +
Rule IDRule NameDelete Email EnabledAction
{{ rule.id }}{{ rule.display_name }}{{ rule.delete_enabled or "None" }}{{ rule.actions }}
+
+ {% endif %} + {% endfor %} +
+{% endblock %} diff --git a/templates/office365_resolve_name.html b/templates/office365_resolve_name.html new file mode 100644 index 0000000..4c94d6b --- /dev/null +++ b/templates/office365_resolve_name.html @@ -0,0 +1,107 @@ + +{% extends 'base/logo_header.html' %} +{% block widget_content %} + +
+ {% for result in results %} + {% if not result.data %} +

No data found

+ {% else %} +
+

Action Parameters

+ + + + + +
Email Address{{ result.email }}
+
+
+

Data

+ + + + + + + + + + + {% for contact in result.contacts %} + + + + + + + {% endfor %} + +
IDPrincipal NameDisplay NameEmail Address
{{ contact.id or "None" }}{{ contact.user_principal_name or "None" }}{{ contact.display_name or "None" }}{{ contact.email_address or "None" }}
+
+ {% endif %} + {% endfor %} +
+{% endblock %} diff --git a/templates/office365_run_query.html b/templates/office365_run_query.html new file mode 100644 index 0000000..4c94935 --- /dev/null +++ b/templates/office365_run_query.html @@ -0,0 +1,153 @@ + +{% extends 'base/logo_header.html' %} +{% block widget_content %} + +
+ {% for result in results %} + {% if not result.data %} +

No data found

+ {% else %} +
+

Action Parameters

+ + + + + + {% if result.param_folder %} + + + + + {% endif %} + {% if result.param_get_folder_id %} + + + + + {% endif %} + {% if result.param_subject %} + + + + + {% endif %} + {% if result.param_body %} + + + + + {% endif %} + {% if result.param_sender %} + + + + + {% endif %} + {% if result.param_limit %} + + + + + {% endif %} + {% if result.param_internet_message_id %} + + + + + {% endif %} +
Email Address{{ result.param_email_address }}
Folder{{ result.param_folder }}
Get Folder ID{{ result.param_get_folder_id }}
Subject{{ result.param_subject }}
Body{{ result.param_body }}
Sender{{ result.param_sender }}
Limit{{ result.param_limit }}
Internet Message ID{{ result.param_internet_message_id }}
+
+
+

Data

+ + + + + + + + + + + + + {% for email in result.emails %} + + + + + + + + + {% endfor %} + +
SenderReceived DateTimeSubjectBody PreviewMessage IDInternet Message ID
{{ email.sender_address or "None" }}{{ email.received_date_time }}{{ email.subject or "None" }}{{ email.body_preview }}{{ email.id }}{{ email.internet_message_id }}
+
+ {% endif %} + {% endfor %} +
+{% endblock %} diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..5e174e4 --- /dev/null +++ b/uv.lock @@ -0,0 +1,1176 @@ +version = 1 +revision = 3 +requires-python = ">=3.13, <3.15" +resolution-markers = [ + "python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.14' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'arm64' and sys_platform == 'darwin'", +] +supported-markers = [ + "python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version < '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.14' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'arm64' and sys_platform == 'darwin'", +] +required-markers = [ + "python_full_version < '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'linux'", +] + +[[package]] +name = "annotated-doc" +version = "0.0.4" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "idna", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, +] + +[[package]] +name = "authlib" +version = "1.6.9" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "cryptography", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/98/00d3dd826d46959ad8e32af2dbb2398868fd9fd0683c26e56d0789bd0e68/authlib-1.6.9.tar.gz", hash = "sha256:d8f2421e7e5980cc1ddb4e32d3f5fa659cfaf60d8eaf3281ebed192e4ab74f04", size = 165134, upload-time = "2026-03-02T07:44:01.998Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/23/b65f568ed0c22f1efacb744d2db1a33c8068f384b8c9b482b52ebdbc3ef6/authlib-1.6.9-py2.py3-none-any.whl", hash = "sha256:f08b4c14e08f0861dc18a32357b33fbcfd2ea86cfe3fe149484b4d764c4a0ac3", size = 244197, upload-time = "2026-03-02T07:44:00.307Z" }, +] + +[[package]] +name = "beautifulsoup4" +version = "4.13.5" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "soupsieve", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/2e/3e5079847e653b1f6dc647aa24549d68c6addb4c595cc0d902d1b19308ad/beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695", size = 622954, upload-time = "2025-08-24T14:06:13.168Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/eb/f4151e0c7377a6e08a38108609ba5cede57986802757848688aeedd1b9e8/beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a", size = 105113, upload-time = "2025-08-24T14:06:14.884Z" }, +] + +[[package]] +name = "bleach" +version = "6.3.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "webencodings", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/07/18/3c8523962314be6bf4c8989c79ad9531c825210dd13a8669f6b84336e8bd/bleach-6.3.0.tar.gz", hash = "sha256:6f3b91b1c0a02bb9a78b5a454c92506aa0fdf197e1d5e114d2e00c6f64306d22", size = 203533, upload-time = "2025-10-27T17:57:39.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/3a/577b549de0cc09d95f11087ee63c739bba856cd3952697eec4c4bb91350a/bleach-6.3.0-py3-none-any.whl", hash = "sha256:fe10ec77c93ddf3d13a73b035abaac7a9f5e436513864ccdad516693213c65d6", size = 164437, upload-time = "2025-10-27T17:57:37.538Z" }, +] + +[[package]] +name = "build" +version = "1.4.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "packaging", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pyproject-hooks", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "pycparser", marker = "(implementation_name != 'PyPy' and platform_machine == 'arm64' and sys_platform == 'darwin') or (implementation_name != 'PyPy' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (implementation_name != 'PyPy' and platform_machine == 'aarch64' and sys_platform == 'linux') or (implementation_name != 'PyPy' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, +] + +[[package]] +name = "cfgv" +version = "3.5.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/b5/721b8799b04bf9afe054a3899c6cf4e880fcf8563cc71c15610242490a0c/cfgv-3.5.0.tar.gz", hash = "sha256:d5b1034354820651caa73ede66a6294d6e95c1b00acc5e9b098e917404669132", size = 7334, upload-time = "2025-11-19T20:55:51.612Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", hash = "sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", size = 7445, upload-time = "2025-11-19T20:55:50.744Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.6" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, + { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, + { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, + { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, + { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, + { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, + { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, + { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, + { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, + { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, + { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, + { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, + { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "colorclass" +version = "2.2.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/1a/31ff00a33569a3b59d65bbdc445c73e12f92ad28195b7ace299f68b9af70/colorclass-2.2.2.tar.gz", hash = "sha256:6d4fe287766166a98ca7bc6f6312daf04a0481b1eda43e7173484051c0ab4366", size = 16709, upload-time = "2021-12-09T00:41:35.661Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/30/b6/daf3e2976932da4ed3579cff7a30a53d22ea9323ee4f0d8e43be60454897/colorclass-2.2.2-py2.py3-none-any.whl", hash = "sha256:6f10c273a0ef7a1150b1120b6095cbdd68e5cf36dfd5d0fc957a2500bbf99a55", size = 18995, upload-time = "2021-12-09T00:41:34.653Z" }, +] + +[[package]] +name = "compressed-rtf" +version = "1.0.7" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/0c/929a4e8ef9d7143f54d77dadb5f370cc7b98534b1bd6e1124d0abe8efb24/compressed_rtf-1.0.7.tar.gz", hash = "sha256:7c30859334839f3cdc7d10796af5b434bb326b9df7cb5a65e95a8eacb2951b0e", size = 8152, upload-time = "2025-03-24T22:39:32.062Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/1d/62f5bf92e12335eb63517f42671ed78512d48bbc69e02a942dd7b90f03f0/compressed_rtf-1.0.7-py3-none-any.whl", hash = "sha256:b7904921d78c67a0a4b7fff9fb361a00ae2b447b6edca010ce321cd98fa0fcc0", size = 7968, upload-time = "2025-03-24T23:03:57.433Z" }, +] + +[[package]] +name = "coverage" +version = "7.13.5" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/e0/70553e3000e345daff267cec284ce4cbf3fc141b6da229ac52775b5428f1/coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179", size = 915967, upload-time = "2026-03-17T10:33:18.341Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/8c/74fedc9663dcf168b0a059d4ea756ecae4da77a489048f94b5f512a8d0b3/coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1", size = 219576, upload-time = "2026-03-17T10:31:09.045Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c9/44fb661c55062f0818a6ffd2685c67aa30816200d5f2817543717d4b92eb/coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3", size = 219942, upload-time = "2026-03-17T10:31:10.708Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/1666e3a4462f8202d836920114fa7a5ee9275d1fa45366d336c551a162dd/coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3", size = 253541, upload-time = "2026-03-17T10:31:14.247Z" }, + { url = "https://files.pythonhosted.org/packages/4e/5e/3ee3b835647be646dcf3c65a7c6c18f87c27326a858f72ab22c12730773d/coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b", size = 254780, upload-time = "2026-03-17T10:31:16.193Z" }, + { url = "https://files.pythonhosted.org/packages/b7/62/5502b73b97aa2e53ea22a39cf8649ff44827bef76d90bf638777daa27a9d/coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161", size = 252908, upload-time = "2026-03-17T10:31:21.312Z" }, + { url = "https://files.pythonhosted.org/packages/46/0d/866d1f74f0acddbb906db212e096dee77a8e2158ca5e6bb44729f9d93298/coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17", size = 252469, upload-time = "2026-03-17T10:31:29.472Z" }, + { url = "https://files.pythonhosted.org/packages/23/d2/17879af479df7fbbd44bd528a31692a48f6b25055d16482fdf5cdb633805/coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d", size = 220262, upload-time = "2026-03-17T10:31:37.184Z" }, + { url = "https://files.pythonhosted.org/packages/5b/4c/d20e554f988c8f91d6a02c5118f9abbbf73a8768a3048cb4962230d5743f/coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0", size = 220617, upload-time = "2026-03-17T10:31:39.245Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f6/7f1ab39393eeb50cfe4747ae8ef0e4fc564b989225aa1152e13a180d74f8/coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3", size = 263987, upload-time = "2026-03-17T10:31:43.724Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d7/62c084fb489ed9c6fbdf57e006752e7c516ea46fd690e5ed8b8617c7d52e/coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9", size = 266416, upload-time = "2026-03-17T10:31:45.769Z" }, + { url = "https://files.pythonhosted.org/packages/2c/16/2e79106d5749bcaf3aee6d309123548e3276517cd7851faa8da213bc61bf/coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e", size = 263981, upload-time = "2026-03-17T10:31:51.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/c6/9bb9ef55903e628033560885f5c31aa227e46878118b63ab15dc7ba87797/coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2", size = 262688, upload-time = "2026-03-17T10:32:00.141Z" }, + { url = "https://files.pythonhosted.org/packages/8e/77/39703f0d1d4b478bfd30191d3c14f53caf596fac00efb3f8f6ee23646439/coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f", size = 219621, upload-time = "2026-03-17T10:32:08.589Z" }, + { url = "https://files.pythonhosted.org/packages/e2/3e/51dff36d99ae14639a133d9b164d63e628532e2974d8b1edb99dd1ebc733/coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e", size = 219953, upload-time = "2026-03-17T10:32:10.507Z" }, + { url = "https://files.pythonhosted.org/packages/22/e5/06b1f88f42a5a99df42ce61208bdec3bddb3d261412874280a19796fc09c/coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510", size = 253503, upload-time = "2026-03-17T10:32:14.449Z" }, + { url = "https://files.pythonhosted.org/packages/80/28/2a148a51e5907e504fa7b85490277734e6771d8844ebcc48764a15e28155/coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247", size = 254852, upload-time = "2026-03-17T10:32:16.56Z" }, + { url = "https://files.pythonhosted.org/packages/f8/66/6ea21f910e92d69ef0b1c3346ea5922a51bad4446c9126db2ae96ee24c4c/coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882", size = 252858, upload-time = "2026-03-17T10:32:23.506Z" }, + { url = "https://files.pythonhosted.org/packages/fc/7b/dc1776b0464145a929deed214aef9fb1493f159b59ff3c7eeeedf91eddd0/coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0", size = 252295, upload-time = "2026-03-17T10:32:31.981Z" }, + { url = "https://files.pythonhosted.org/packages/60/5b/4a168591057b3668c2428bff25dd3ebc21b629d666d90bcdfa0217940e84/coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b", size = 220351, upload-time = "2026-03-17T10:32:41.196Z" }, + { url = "https://files.pythonhosted.org/packages/f5/21/1fd5c4dbfe4a58b6b99649125635df46decdfd4a784c3cd6d410d303e370/coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c", size = 220612, upload-time = "2026-03-17T10:32:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0d/c8928f2bd518c45990fe1a2ab8db42e914ef9b726c975facc4282578c3eb/coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29", size = 264107, upload-time = "2026-03-17T10:32:47.971Z" }, + { url = "https://files.pythonhosted.org/packages/ef/ae/4ae35bbd9a0af9d820362751f0766582833c211224b38665c0f8de3d487f/coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607", size = 266513, upload-time = "2026-03-17T10:32:50.1Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d8/49a72d6de146eebb0b7e48cc0f4bc2c0dd858e3d4790ab2b39a2872b62bd/coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab", size = 263982, upload-time = "2026-03-17T10:32:56.803Z" }, + { url = "https://files.pythonhosted.org/packages/48/11/07f413dba62db21fb3fad5d0de013a50e073cc4e2dc4306e770360f6dfc8/coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a", size = 262745, upload-time = "2026-03-17T10:33:06.285Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/a4cf96b8ce1e566ed238f0659ac2d3f007ed1d14b181bcb684e19561a69a/coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61", size = 211346, upload-time = "2026-03-17T10:33:15.691Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.5" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "cffi", marker = "(platform_machine == 'arm64' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_machine == 'x86_64' and platform_python_implementation != 'PyPy' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, + { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, + { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, + { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, + { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + +[[package]] +name = "docopt" +version = "0.6.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491", size = 25901, upload-time = "2014-06-16T11:18:57.406Z" } + +[[package]] +name = "easygui" +version = "0.98.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/ad/e35f7a30272d322be09dc98592d2f55d27cc933a7fde8baccbbeb2bd9409/easygui-0.98.3.tar.gz", hash = "sha256:d653ff79ee1f42f63b5a090f2f98ce02335d86ad8963b3ce2661805cafe99a04", size = 85583, upload-time = "2022-04-01T13:15:50.752Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/a7/b276ff776533b423710a285c8168b52551cb2ab0855443131fdc7fd8c16f/easygui-0.98.3-py2.py3-none-any.whl", hash = "sha256:33498710c68b5376b459cd3fc48d1d1f33822139eb3ed01defbc0528326da3ba", size = 92655, upload-time = "2022-04-01T13:15:49.568Z" }, +] + +[[package]] +name = "ebcdic" +version = "1.1.1" +source = { registry = "https://pypi.python.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/2f/633031205333bee5f9f93761af8268746aa75f38754823aabb8570eb245b/ebcdic-1.1.1-py2.py3-none-any.whl", hash = "sha256:33b4cb729bc2d0bf46cc1847b0e5946897cb8d3f53520c5b9aa5fa98d7e735f1", size = 128537, upload-time = "2019-08-09T00:54:35.544Z" }, +] + +[[package]] +name = "extract-msg" +version = "0.55.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "beautifulsoup4", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "compressed-rtf", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "ebcdic", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "olefile", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "red-black-tree-mod", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "rtfde", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "tzlocal", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/65/c70afb3b119a44b3ee36b029485dc15326cf3a7c50da19a1ecbbf949c5d1/extract_msg-0.55.0.tar.gz", hash = "sha256:cf08283498c3dfcc7f894dad1579f52e3ced9fb76b865c2355cbe757af8a54e1", size = 331170, upload-time = "2025-08-12T16:07:56.537Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/53/81/87d5241036046ea17c5c8db228f4c9e04e07e53b627015d4496a99449aaf/extract_msg-0.55.0-py3-none-any.whl", hash = "sha256:baf0cdee9a8d267b70c366bc57ceb03dbfa1e7ab2dca6824169a7fe623f0917c", size = 336033, upload-time = "2025-08-12T16:07:54.886Z" }, +] + +[[package]] +name = "filelock" +version = "3.25.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, +] + +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" }, +] + +[[package]] +name = "hatchling" +version = "1.29.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "packaging", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pathspec", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pluggy", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "trove-classifiers", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/9c/b4cfe330cd4f49cff17fd771154730555fa4123beb7f292cf0098b4e6c20/hatchling-1.29.0.tar.gz", hash = "sha256:793c31816d952cee405b83488ce001c719f325d9cda69f1fc4cd750527640ea6", size = 55656, upload-time = "2026-02-23T19:42:06.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/8a/44032265776062a89171285ede55a0bdaadc8ac00f27f0512a71a9e3e1c8/hatchling-1.29.0-py3-none-any.whl", hash = "sha256:50af9343281f34785fab12da82e445ed987a6efb34fd8c2fc0f6e6630dbcc1b0", size = 76356, upload-time = "2026-02-23T19:42:05.197Z" }, +] + +[[package]] +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "certifi", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "h11", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "anyio", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "certifi", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "httpcore", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "idna", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" }, +] + +[[package]] +name = "httpx-retries" +version = "0.4.6" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "httpx", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/13/5eac2df576c02280f79e4639a6d4c93a25cfe94458275f5aa55f5e6c8ea0/httpx_retries-0.4.6.tar.gz", hash = "sha256:a076d8a5ede5d5794e9c241da17b15b393b482129ddd2fdf1fa56a3fa1f28a7f", size = 13466, upload-time = "2026-02-17T16:16:05.995Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/97/63f56da4400034adde22adfe7524635dba068f17d6858f92ecd96f55b53e/httpx_retries-0.4.6-py3-none-any.whl", hash = "sha256:d66d912173b844e065ffb109345a453b922f4c2cd9c9e11139304cb33e7a1ee1", size = 8490, upload-time = "2026-02-17T16:16:04.137Z" }, +] + +[[package]] +name = "humanize" +version = "4.15.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/66/a3921783d54be8a6870ac4ccffcd15c4dc0dd7fcce51c6d63b8c63935276/humanize-4.15.0.tar.gz", hash = "sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10", size = 83599, upload-time = "2025-12-20T20:16:13.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" }, +] + +[[package]] +name = "identify" +version = "2.6.18" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/c4/7fb4db12296cdb11893d61c92048fe617ee853f8523b9b296ac03b43757e/identify-2.6.18.tar.gz", hash = "sha256:873ac56a5e3fd63e7438a7ecbc4d91aca692eb3fefa4534db2b7913f3fc352fd", size = 99580, upload-time = "2026-03-15T18:39:50.319Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/33/92ef41c6fad0233e41d3d84ba8e8ad18d1780f1e5d99b3c683e6d7f98b63/identify-2.6.18-py2.py3-none-any.whl", hash = "sha256:8db9d3c8ea9079db92cafb0ebf97abdc09d52e97f4dcf773a2e694048b7cd737", size = 99394, upload-time = "2026-03-15T18:39:48.915Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "markupsafe", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + +[[package]] +name = "lark" +version = "1.3.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732, upload-time = "2025-10-27T18:25:56.653Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151, upload-time = "2025-10-27T18:25:54.882Z" }, +] + +[[package]] +name = "librt" +version = "0.8.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, + { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, + { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, + { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" }, + { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" }, + { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" }, + { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" }, + { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" }, + { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" }, + { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "mdurl", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "msal" +version = "1.35.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "cryptography", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pyjwt", extra = ["crypto"], marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "requests", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/aa/5a646093ac218e4a329391d5a31e5092a89db7d2ef1637a90b82cd0b6f94/msal-1.35.1.tar.gz", hash = "sha256:70cac18ab80a053bff86219ba64cfe3da1f307c74b009e2da57ef040eb1b5656", size = 165658, upload-time = "2026-03-04T23:38:51.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/86/16815fddf056ca998853c6dc525397edf0b43559bb4073a80d2bc7fe8009/msal-1.35.1-py3-none-any.whl", hash = "sha256:8f4e82f34b10c19e326ec69f44dc6b30171f2f7098f3720ea8a9f0c11832caa3", size = 119909, upload-time = "2026-03-04T23:38:50.452Z" }, +] + +[[package]] +name = "msgraphoffice365" +version = "4.1.0" +source = { virtual = "." } +dependencies = [ + { name = "beautifulsoup4", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "msal", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "requests", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "splunk-soar-sdk", extra = ["email"], marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] + +[package.dev-dependencies] +dev = [ + { name = "coverage", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "mypy", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pre-commit", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest-mock", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest-watch", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "ruff", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] + +[package.metadata] +requires-dist = [ + { name = "beautifulsoup4", specifier = ">=4.13.0,<4.14.0" }, + { name = "msal", specifier = ">=1.35.1" }, + { name = "requests", specifier = ">=2.32.5" }, + { name = "splunk-soar-sdk", extras = ["email"], specifier = ">=3.18.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", specifier = ">=7.6.7,<8" }, + { name = "mypy", specifier = ">=1.2.0,<2" }, + { name = "pre-commit", specifier = ">=4.2.0,<5" }, + { name = "pytest", specifier = ">=7.4.2,<8" }, + { name = "pytest-mock", specifier = ">=3.14.0,<4" }, + { name = "pytest-watch", specifier = ">=4.2.0,<5" }, + { name = "ruff", specifier = ">=0.11.6,<1" }, +] + +[[package]] +name = "msoffcrypto-tool" +version = "6.0.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "cryptography", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "olefile", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/34/6250bdddaeaae24098e45449ea362fb3555a65fba30cad0ad5630ea48d1a/msoffcrypto_tool-6.0.0.tar.gz", hash = "sha256:9a5ebc4c0096b42e5d7ebc2350afdc92dc511061e935ca188468094fdd032bbe", size = 40593, upload-time = "2026-01-12T08:59:56.73Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/85/9e359fa9279e1d6861faaf9b6f037a3226374deb20a054c3937be6992013/msoffcrypto_tool-6.0.0-py3-none-any.whl", hash = "sha256:46c394ed5d9641e802fc79bf3fb0666a53748b23fa8c4aa634ae9d30d46fe397", size = 48791, upload-time = "2026-01-12T08:59:55.394Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "librt", marker = "(platform_machine == 'arm64' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and platform_python_implementation != 'PyPy' and sys_platform == 'linux') or (platform_machine == 'x86_64' and platform_python_implementation != 'PyPy' and sys_platform == 'linux')" }, + { name = "mypy-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pathspec", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "olefile" +version = "0.47" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/69/1b/077b508e3e500e1629d366249c3ccb32f95e50258b231705c09e3c7a4366/olefile-0.47.zip", hash = "sha256:599383381a0bf3dfbd932ca0ca6515acd174ed48870cbf7fee123d698c192c1c", size = 112240, upload-time = "2023-12-01T16:22:53.025Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/d3/b64c356a907242d719fc668b71befd73324e47ab46c8ebbbede252c154b2/olefile-0.47-py2.py3-none-any.whl", hash = "sha256:543c7da2a7adadf21214938bb79c83ea12b473a4b6ee4ad4bf854e7715e13d1f", size = 114565, upload-time = "2023-12-01T16:22:51.518Z" }, +] + +[[package]] +name = "oletools" +version = "0.60.2" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "colorclass", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "easygui", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "msoffcrypto-tool", marker = "(platform_machine == 'arm64' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and platform_python_implementation != 'PyPy' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "olefile", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pcodedmp", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pyparsing", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/2f/037f40e44706d542b94a2312ccc33ee2701ebfc9a83b46b55263d49ce55a/oletools-0.60.2.zip", hash = "sha256:ad452099f4695ffd8855113f453348200d195ee9fa341a09e197d66ee7e0b2c3", size = 3433750, upload-time = "2024-07-02T14:50:38.242Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/ff/05257b7183279b80ecec6333744de23f48f0faeeba46c93e6d13ce835515/oletools-0.60.2-py2.py3-none-any.whl", hash = "sha256:72ad8bd748fd0c4e7b5b4733af770d11543ebb2bf2697455f99f975fcd50cc96", size = 989449, upload-time = "2024-07-02T14:50:29.122Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "pcodedmp" +version = "1.2.6" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "oletools", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3d/20/6d461e29135f474408d0d7f95b2456a9ba245560768ee51b788af10f7429/pcodedmp-1.2.6.tar.gz", hash = "sha256:025f8c809a126f45a082ffa820893e6a8d990d9d7ddb68694b5a9f0a6dbcd955", size = 35549, upload-time = "2019-07-30T18:05:42.516Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/72/b380fb5c89d89c3afafac8cf02a71a45f4f4a4f35531ca949a34683962d1/pcodedmp-1.2.6-py2.py3-none-any.whl", hash = "sha256:4441f7c0ab4cbda27bd4668db3b14f36261d86e5059ce06c0828602cbe1c4278", size = 30939, upload-time = "2019-07-30T18:05:40.483Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.4" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.5.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "cfgv", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "identify", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "nodeenv", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pyyaml", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "virtualenv", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "annotated-types", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pydantic-core", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typing-inspection", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + +[[package]] +name = "pytest" +version = "7.4.4" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "iniconfig", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "packaging", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pluggy", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/1f/9d8e98e4133ffb16c90f3b405c43e38d3abb715bb5d7a63a5a684f7e46a3/pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280", size = 1357116, upload-time = "2023-12-31T12:00:18.035Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/ff/f6e8b8f39e08547faece4bd80f89d5a8de68a38b2d179cc1c4490ffa3286/pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8", size = 325287, upload-time = "2023-12-31T12:00:13.963Z" }, +] + +[[package]] +name = "pytest-mock" +version = "3.15.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "pytest", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/14/eb014d26be205d38ad5ad20d9a80f7d201472e08167f0bb4361e251084a9/pytest_mock-3.15.1.tar.gz", hash = "sha256:1849a238f6f396da19762269de72cb1814ab44416fa73a8686deac10b0d87a0f", size = 34036, upload-time = "2025-09-16T16:37:27.081Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/cc/06253936f4a7fa2e0f48dfe6d851d9c56df896a9ab09ac019d70b760619c/pytest_mock-3.15.1-py3-none-any.whl", hash = "sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d", size = 10095, upload-time = "2025-09-16T16:37:25.734Z" }, +] + +[[package]] +name = "pytest-watch" +version = "4.2.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "colorama", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "docopt", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pytest", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "watchdog", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/47/ab65fc1d682befc318c439940f81a0de1026048479f732e84fe714cd69c0/pytest-watch-4.2.0.tar.gz", hash = "sha256:06136f03d5b361718b8d0d234042f7b2f203910d8568f63df2f866b547b3d4b9", size = 16340, upload-time = "2018-05-20T19:52:16.194Z" } + +[[package]] +name = "python-discovery" +version = "1.2.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "filelock", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "platformdirs", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/90/bcce6b46823c9bec1757c964dc37ed332579be512e17a30e9698095dcae4/python_discovery-1.2.0.tar.gz", hash = "sha256:7d33e350704818b09e3da2bd419d37e21e7c30db6e0977bb438916e06b41b5b1", size = 58055, upload-time = "2026-03-19T01:43:08.248Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/3c/2005227cb951df502412de2fa781f800663cccbef8d90ec6f1b371ac2c0d/python_discovery-1.2.0-py3-none-any.whl", hash = "sha256:1e108f1bbe2ed0ef089823d28805d5ad32be8e734b86a5f212bf89b71c266e4a", size = 31524, upload-time = "2026-03-19T01:43:07.045Z" }, +] + +[[package]] +name = "python-magic" +version = "0.4.27" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/db/0b3e28ac047452d079d375ec6798bf76a036a08182dbb39ed38116a49130/python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b", size = 14677, upload-time = "2022-06-07T20:16:59.508Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/73/9f872cb81fc5c3bb48f7227872c28975f998f3e7c2b1c16e95e6432bbb90/python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3", size = 13840, upload-time = "2022-06-07T20:16:57.763Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" }, + { url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" }, + { url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" }, + { url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" }, + { url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" }, + { url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, +] + +[[package]] +name = "red-black-tree-mod" +version = "1.22" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/75/bfa342a2ebfc9623b701f1c6995b9906fd6dd2cedf6bce777d09e23303ac/red-black-tree-mod-1.22.tar.gz", hash = "sha256:38e3652903a2bf96379c27c2082ca0b7b905158662dd7ef0c97f4fd93a9aa908", size = 34173, upload-time = "2023-12-26T14:00:22.056Z" } + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "certifi", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "charset-normalizer", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "idna", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "urllib3", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "markdown-it-py", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pygments", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "rtfde" +version = "0.1.2.2" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "lark", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "oletools", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/5c/116a016b38af589e8141160bc9b034b73dde2e50c22a921751f4d982a7ca/rtfde-0.1.2.2.tar.gz", hash = "sha256:2f0cd6ecd644071e39452e6fc4f4a1435453af0ec7c90ea86fb4fc96010c7f1b", size = 33408, upload-time = "2025-12-09T17:10:31.805Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/24/5a653278259be44c1845ddd56dd30cfa7265281ba149b9342b79f9d4f788/rtfde-0.1.2.2-py3-none-any.whl", hash = "sha256:d43868c74f21ae9ea5acbfd4176d5de1f2cfae0ff7f267698471c606287c04ec", size = 36713, upload-time = "2025-12-09T17:10:30.893Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.7" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/22/9e4f66ee588588dc6c9af6a994e12d26e19efbe874d1a909d09a6dac7a59/ruff-0.15.7.tar.gz", hash = "sha256:04f1ae61fc20fe0b148617c324d9d009b5f63412c0b16474f3d5f1a1a665f7ac", size = 4601277, upload-time = "2026-03-19T16:26:22.605Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/4a/82e0fa632e5c8b1eba5ee86ecd929e8ff327bbdbfb3c6ac5d81631bef605/ruff-0.15.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:722d165bd52403f3bdabc0ce9e41fc47070ac56d7a91b4e0d097b516a53a3477", size = 10955433, upload-time = "2026-03-19T16:27:00.205Z" }, + { url = "https://files.pythonhosted.org/packages/ab/10/12586735d0ff42526ad78c049bf51d7428618c8b5c467e72508c694119df/ruff-0.15.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fbc2448094262552146cbe1b9643a92f66559d3761f1ad0656d4991491af49e", size = 10269302, upload-time = "2026-03-19T16:26:26.183Z" }, + { url = "https://files.pythonhosted.org/packages/eb/5d/32b5c44ccf149a26623671df49cbfbd0a0ae511ff3df9d9d2426966a8d57/ruff-0.15.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b39329b60eba44156d138275323cc726bbfbddcec3063da57caa8a8b1d50adf", size = 10607625, upload-time = "2026-03-19T16:27:03.263Z" }, + { url = "https://files.pythonhosted.org/packages/b0/99/66f0343176d5eab02c3f7fcd2de7a8e0dd7a41f0d982bee56cd1c24db62b/ruff-0.15.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dce0896488562f09a27b9c91b1f58a097457143931f3c4d519690dea54e624c5", size = 11242422, upload-time = "2026-03-19T16:26:29.277Z" }, + { url = "https://files.pythonhosted.org/packages/a7/53/90fbb9e08b29c048c403558d3cdd0adf2668b02ce9d50602452e187cd4af/ruff-0.15.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5f3e4b221fb4bd293f79912fc5e93a9063ebd6d0dcbd528f91b89172a9b8436c", size = 10577861, upload-time = "2026-03-19T16:26:57.459Z" }, + { url = "https://files.pythonhosted.org/packages/bf/29/a4ae78394f76c7759953c47884eb44de271b03a66634148d9f7d11e721bd/ruff-0.15.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:112c1fa316a558bb34319282c1200a8bf0495f1b735aeb78bfcb2991e6087580", size = 11336961, upload-time = "2026-03-19T16:26:39.076Z" }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316, upload-time = "2026-03-09T12:47:17.221Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223, upload-time = "2026-03-09T12:47:15.026Z" }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, +] + +[[package]] +name = "soupsieve" +version = "2.8.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, +] + +[[package]] +name = "splunk-soar-sdk" +version = "3.18.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "authlib", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "beautifulsoup4", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "bleach", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "build", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "click", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "distro", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "extract-msg", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "hatchling", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "httpx", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "httpx-retries", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "humanize", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "jinja2", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "packaging", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pydantic", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "pyjwt", extra = ["crypto"], marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "requests", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "setuptools", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "toml", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "tqdm", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "typer", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/d8/fc5e6212ba2745337d15a5eb229d25ff718d42de89095effa7476a247e75/splunk_soar_sdk-3.18.0.tar.gz", hash = "sha256:f6b92322dade093c85ae87bbb5faf4406f5a80638b9a2236c84f5a24d52ca3ae", size = 647994, upload-time = "2026-03-23T23:56:20.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/f4/ead7f0948a9c8f9fc82e13cc50c7db47b059257db63509e9921b6eb5b012/splunk_soar_sdk-3.18.0-py3-none-any.whl", hash = "sha256:c5772091b142bedba5fff027f3b202a97f17f40c6a4a12778a9a3616ac74a149", size = 206774, upload-time = "2026-03-23T23:56:22.221Z" }, +] + +[package.optional-dependencies] +email = [ + { name = "python-magic", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, +] + +[[package]] +name = "tqdm" +version = "4.67.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, +] + +[[package]] +name = "trove-classifiers" +version = "2026.1.14.14" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/43/7935f8ea93fcb6680bc10a6fdbf534075c198eeead59150dd5ed68449642/trove_classifiers-2026.1.14.14.tar.gz", hash = "sha256:00492545a1402b09d4858605ba190ea33243d361e2b01c9c296ce06b5c3325f3", size = 16997, upload-time = "2026-01-14T14:54:50.526Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bb/4a/2e5583e544bc437d5e8e54b47db87430df9031b29b48d17f26d129fa60c0/trove_classifiers-2026.1.14.14-py3-none-any.whl", hash = "sha256:1f9553927f18d0513d8e5ff80ab8980b8202ce37ecae0e3274ed2ef11880e74d", size = 14197, upload-time = "2026-01-14T14:54:49.067Z" }, +] + +[[package]] +name = "typer" +version = "0.23.1" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "annotated-doc", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "click", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "rich", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "shellingham", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/07/b822e1b307d40e263e8253d2384cf98c51aa2368cc7ba9a07e523a1d964b/typer-0.23.1.tar.gz", hash = "sha256:2070374e4d31c83e7b61362fd859aa683576432fd5b026b060ad6b4cd3b86134", size = 120047, upload-time = "2026-02-13T10:04:30.984Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/91/9b286ab899c008c2cb05e8be99814807e7fbbd33f0c0c960470826e5ac82/typer-0.23.1-py3-none-any.whl", hash = "sha256:3291ad0d3c701cbf522012faccfbb29352ff16ad262db2139e6b01f15781f14e", size = 56813, upload-time = "2026-02-13T10:04:32.008Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "tzlocal" +version = "5.3.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/2e/c14812d3d4d9cd1773c6be938f89e5735a1f11a9f184ac3639b93cef35d5/tzlocal-5.3.1.tar.gz", hash = "sha256:cceffc7edecefea1f595541dbd6e990cb1ea3d19bf01b2809f362a03dd7921fd", size = 30761, upload-time = "2025-03-05T21:17:41.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/14/e2a54fabd4f08cd7af1c07030603c3356b74da07f7cc056e600436edfa17/tzlocal-5.3.1-py3-none-any.whl", hash = "sha256:eb1a66c3ef5847adf7a834f1be0800581b683b5608e74f86ecbcef8ab91bb85d", size = 18026, upload-time = "2025-03-05T21:17:39.857Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "virtualenv" +version = "21.2.0" +source = { registry = "https://pypi.python.org/simple" } +dependencies = [ + { name = "distlib", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "filelock", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "platformdirs", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "python-discovery", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or (platform_machine == 'x86_64' and sys_platform == 'darwin') or (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +source = { registry = "https://pypi.python.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, +] diff --git a/wheels/py3/PyJWT-2.10.1-py3-none-any.whl b/wheels/py3/PyJWT-2.10.1-py3-none-any.whl deleted file mode 100644 index 6195c57..0000000 Binary files a/wheels/py3/PyJWT-2.10.1-py3-none-any.whl and /dev/null differ diff --git a/wheels/py3/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl b/wheels/py3/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl deleted file mode 100644 index 823e2c8..0000000 Binary files a/wheels/py3/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl and /dev/null differ diff --git a/wheels/py3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl b/wheels/py3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl deleted file mode 100644 index ea525ae..0000000 Binary files a/wheels/py3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl and /dev/null differ diff --git a/wheels/py3/msal-1.34.0-py3-none-any.whl b/wheels/py3/msal-1.34.0-py3-none-any.whl deleted file mode 100644 index 80ceed4..0000000 Binary files a/wheels/py3/msal-1.34.0-py3-none-any.whl and /dev/null differ diff --git a/wheels/py3/pycparser-2.23-py3-none-any.whl b/wheels/py3/pycparser-2.23-py3-none-any.whl deleted file mode 100644 index 0748c19..0000000 Binary files a/wheels/py3/pycparser-2.23-py3-none-any.whl and /dev/null differ diff --git a/wheels/py3/typing_extensions-4.15.0-py3-none-any.whl b/wheels/py3/typing_extensions-4.15.0-py3-none-any.whl deleted file mode 100644 index 5fec9ca..0000000 Binary files a/wheels/py3/typing_extensions-4.15.0-py3-none-any.whl and /dev/null differ diff --git a/wheels/py313/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl b/wheels/py313/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl deleted file mode 100644 index c9c3bc6..0000000 Binary files a/wheels/py313/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl and /dev/null differ diff --git a/wheels/py39/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl b/wheels/py39/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl deleted file mode 100644 index 75aa59d..0000000 Binary files a/wheels/py39/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl and /dev/null differ