diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ba1328d..d5021f2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/phantomcyber/dev-cicd-tools - rev: v1.16 + rev: v1.18 hooks: - id: org-hook - id: package-app-dependencies - repo: https://github.com/Yelp/detect-secrets - rev: v1.4.0 + rev: v1.5.0 hooks: - id: detect-secrets args: ['--no-verify', '--exclude-files', '^servicenow.json$'] diff --git a/LICENSE b/LICENSE index bc44b00..b7b3c69 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) 2016-2023 Splunk Inc. + Copyright (c) 2016-2024 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 d79f7f1..0dcf085 100644 --- a/NOTICE +++ b/NOTICE @@ -1,15 +1,8 @@ Splunk SOAR ServiceNow -Copyright (c) 2016-2023 Splunk Inc. +Copyright (c) 2016-2024 Splunk Inc. Third-party Software Attributions: -Library: beautifulsoup4 -Version: 4.9.1 -License: MIT -Copyright 2004-2017 Leonard Richardson -Copyright 2004-2019 Leonard Richardson -Copyright 2018 Isaac Muse - Library: python-magic Version: 0.4.18 License: MIT @@ -21,8 +14,3 @@ License: MIT License: Zope Copyright 1987-2006 implementation only works for dates between Copyright 2003-2019 Stuart Bishop - -Library: requests -Version: 2.25.0 -License: Apache 2.0 -Kenneth Reitz diff --git a/README.md b/README.md index b066aed..950978a 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ # ServiceNow Publisher: Splunk -Connector Version: 2.5.0 +Connector Version: 2.6.0 Product Vendor: ServiceNow Product Name: ServiceNow Product Version Supported (regex): ".\*" -Minimum Product Version: 6.1.0 +Minimum Product Version: 6.2.1 This app integrates with ServiceNow to perform investigative and generic actions @@ -211,6 +211,7 @@ VARIABLE | REQUIRED | TYPE | DESCRIPTION [get variables](#action-get-variables) - Get variables for a ticket/record [run query](#action-run-query) - Gets object data according to the specified query [query users](#action-query-users) - Gets user data according to the specified query, username, or system ID +[search sources](#action-search-sources) - Search for records across multiple tables [on poll](#action-on-poll) - Ingest tickets from SNOW ## action: 'test connectivity' @@ -302,7 +303,7 @@ action_result.data.\*.depreciated_amount | string | | $0.00 action_result.data.\*.depreciation | string | | action_result.data.\*.depreciation_date | string | | action_result.data.\*.description | string | | My computer is not detecting the headphone device. It could be an issue with the USB port. -action_result.data.\*.display_name | string | | SW000077 - Microsoft ASP.NET 2011 +action_result.data.\*.display_name | string | | SW000077 Test action_result.data.\*.disposal_reason | string | | action_result.data.\*.due | string | | action_result.data.\*.due_date | string | | @@ -329,7 +330,7 @@ action_result.data.\*.location | string | | action_result.data.\*.made_sla | string | | true action_result.data.\*.managed_by | string | | action_result.data.\*.merged_into | string | | -action_result.data.\*.model.display_value | string | | Microsoft ASP.NET 2011 +action_result.data.\*.model.display_value | string | | ASP.NET 2011 action_result.data.\*.model.link | string | | https://dev78070.service-now.com/api/now/table/cmdb_model/81bfae3f37601000deeabfc8bcbe5d2d action_result.data.\*.model_category.display_value | string | | Software License action_result.data.\*.model_category.link | string | | https://dev78070.service-now.com/api/now/table/cmdb_model_category/35bf2d4137101000deeabfc8bcbe5dbd @@ -401,7 +402,7 @@ action_result.data.\*.upon_approval | string | | Proceed to Next Task action_result.data.\*.upon_reject | string | | Cancel all future Tasks action_result.data.\*.urgency | string | | 2 - Medium action_result.data.\*.user_input | string | | -action_result.data.\*.vendor.display_value | string | | Microsoft +action_result.data.\*.vendor.display_value | string | | PRB000050 Test action_result.data.\*.vendor.link | string | `url` | https://dev78070.service-now.com/api/now/table/core_company/0e8b8e650a0a0b3b004f285ffbb1a4fc action_result.data.\*.warranty_expiration | string | | action_result.data.\*.watch_list | string | | @@ -1418,7 +1419,7 @@ PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES --------- | ---- | -------- | -------------- action_result.status | string | | success failed -action_result.parameter.description | string | | Investigative actions to check for the presence of phapp_servicenow +action_result.parameter.description | string | | Switch occasionally drops connections action_result.parameter.fields | string | | {"made_sla":true,"severity":3,"description":"This is testing description"} action_result.parameter.short_description | string | | phapp_servicenow, Multiple action need to be taken action_result.parameter.table | string | `servicenow table` | incident @@ -2258,9 +2259,9 @@ action_result.data.\*.work_end | string | | action_result.data.\*.work_notes | string | | action_result.data.\*.work_notes_list | string | | action_result.data.\*.work_start | string | | -action_result.summary.total_tickets | numeric | | 2 68 +action_result.summary.total_tickets | numeric | | 2 action_result.message | string | | Important data: value -summary.total_objects | numeric | | 1 2 +summary.total_objects | numeric | | 1 summary.total_objects_successful | numeric | | 1 ## action: 'query users' @@ -2297,7 +2298,7 @@ action_result.data.\*.date_format | string | | action_result.data.\*.default_perspective | string | | action_result.data.\*.department.link | string | `url` | https://dev116432.service-now.com/api/now/table/cmn_department/a581ab703710200044e0bfc8bcbe5de8 action_result.data.\*.department.value | string | `md5` | a581ab703710200044e0bfc8bcbe5de8 -action_result.data.\*.email | string | `email` | herman@phantom.us +action_result.data.\*.email | string | `email` | abc@pqr.us action_result.data.\*.employee_number | string | | action_result.data.\*.enable_multifactor_authn | string | | false action_result.data.\*.failed_attempts | string | | 0 @@ -2349,6 +2350,81 @@ action_result.message | string | | Total tickets: 1 summary.total_objects | numeric | | 1 summary.total_objects_successful | numeric | | 1 +## action: 'search sources' +Search for records across multiple tables + +Type: **investigate** +Read only: **True** + +To find the list of search source IDs for the sysparm_search_sources parameter, follow this path in servicenow UI: All > Workspace Experience > Administration > Search Sources. Once there, click with two fingers/right click on the source name and copy the sys_id. + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**sysparm_term** | required | Search record for the given term | string | +**sysparm_search_sources** | required | SYS ID of search sources, Comma-separated list allowed | string | `servicenow ticket sysid` + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.sysparm_search_sources | string | `servicenow ticket sysid` | tec681c771testfedf0bcbe2c2606c +action_result.parameter.sysparm_term | string | | Resolved +action_result.data.\*.result_count | numeric | | +action_result.data.\*.search_results.\*.fields.\*.label | string | | Number +action_result.data.\*.search_results.\*.fields.\*.label_plural | string | | Numbers +action_result.data.\*.search_results.\*.fields.\*.max_length | numeric | | 40 +action_result.data.\*.search_results.\*.fields.\*.name | string | | number +action_result.data.\*.search_results.\*.fields.\*.reference | string | | sys_user_group +action_result.data.\*.search_results.\*.fields.\*.type | string | | string +action_result.data.\*.search_results.\*.label | string | | Problem +action_result.data.\*.search_results.\*.limit | numeric | | 20 +action_result.data.\*.search_results.\*.page | numeric | | 1 +action_result.data.\*.search_results.\*.query | string | | 123TEXTQUEtest=Fix Applied +action_result.data.\*.search_results.\*.record_count | numeric | | +action_result.data.\*.search_results.\*.records.\*.data.assignment_group.display | string | | +action_result.data.\*.search_results.\*.records.\*.data.assignment_group.value | string | | +action_result.data.\*.search_results.\*.records.\*.data.caller_id.display | string | | System Administrator +action_result.data.\*.search_results.\*.records.\*.data.caller_id.value | string | | 6816f7test016401c5a33be04be441 +action_result.data.\*.search_results.\*.records.\*.data.category.display | string | | Inquiry / Help +action_result.data.\*.search_results.\*.records.\*.data.category.value | string | | inquiry +action_result.data.\*.search_results.\*.records.\*.data.cmdb_ci.display | string | | +action_result.data.\*.search_results.\*.records.\*.data.cmdb_ci.value | string | | +action_result.data.\*.search_results.\*.records.\*.data.number.display | string | | INC000001 +action_result.data.\*.search_results.\*.records.\*.data.number.value | string | | INC000001 +action_result.data.\*.search_results.\*.records.\*.data.opened_at.display | string | | 2023-04-05 00:59:28 +action_result.data.\*.search_results.\*.records.\*.data.opened_at.value | string | | 2023-04-05 07:59:28 +action_result.data.\*.search_results.\*.records.\*.data.priority.display | string | | 5 - Planning +action_result.data.\*.search_results.\*.records.\*.data.priority.value | string | | 5 +action_result.data.\*.search_results.\*.records.\*.data.related_incidents.display | string | | 0 +action_result.data.\*.search_results.\*.records.\*.data.related_incidents.value | string | | 0 +action_result.data.\*.search_results.\*.records.\*.data.resolution_code.value | string | | +action_result.data.\*.search_results.\*.records.\*.data.state.display | string | | New +action_result.data.\*.search_results.\*.records.\*.data.state.value | string | | 1 +action_result.data.\*.search_results.\*.records.\*.data.sys_id.display | string | | c673edctest1106401f1e3f153af11 +action_result.data.\*.search_results.\*.records.\*.data.sys_id.value | string | | td673test1106401f1e3f153af11 +action_result.data.\*.search_results.\*.records.\*.metadata.description | string | | +action_result.data.\*.search_results.\*.records.\*.metadata.thumbnail_url | string | | +action_result.data.\*.search_results.\*.records.\*.metadata.title | string | | hello +action_result.data.\*.search_results.\*.records.\*.record_class_name | string | | incident +action_result.data.\*.search_results.\*.records.\*.record_url | string | | /incident.do?sys_id=test978221106401f1e99f11&sysparm_view=text_search +action_result.data.\*.search_results.\*.records.\*.sys_id | string | | c673ettest97822119953af11 +action_result.data.\*.search_results.\*.records.\*.table | string | | incident +action_result.data.\*.search_results.\*.sys_id | string | | test897862996401f1e3f1990e +action_result.data.\*.search_results.\*.term | string | | Resolved +action_result.data.\*.sources.\*.condition.display | string | | +action_result.data.\*.sources.\*.condition.value | string | | +action_result.data.\*.sources.\*.name.display | string | | Tasks-Tickets +action_result.data.\*.sources.\*.name.value | string | | Tasks-Tickets +action_result.data.\*.sources.\*.source_table | string | | Problem +action_result.data.\*.sources.\*.sys_id | string | `servicenow ticket sysid` | test8699964099f153af0e99 +action_result.data.\*.term | string | | Resolved +action_result.summary | string | | +action_result.summary.total_records | numeric | | 1 +action_result.message | string | | Total records: 0 +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + ## action: 'on poll' Ingest tickets from SNOW diff --git a/__init__.py b/__init__.py index bc35686..d7d82bb 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,6 @@ # File: __init__.py # -# Copyright (c) 2016-2023 Splunk Inc. +# Copyright (c) 2016-2024 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/release_notes/2.6.0.md b/release_notes/2.6.0.md new file mode 100644 index 0000000..71a5c08 --- /dev/null +++ b/release_notes/2.6.0.md @@ -0,0 +1,2 @@ +* Fixed bug in pagination logic [PAPP-33317] +* Added new 'search sources' action support [PAPP-30131] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5afb020..a07a7ad 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -beautifulsoup4==4.9.1 python-magic==0.4.18 pytz==2021.1 diff --git a/servicenow.json b/servicenow.json index f66c117..689fa65 100644 --- a/servicenow.json +++ b/servicenow.json @@ -6,15 +6,15 @@ "logo_dark": "logo_servicenow_dark.svg", "logo": "logo_servicenow.svg", "publisher": "Splunk", - "license": "Copyright (c) 2016-2023 Splunk Inc.", + "license": "Copyright (c) 2016-2024 Splunk Inc.", "type": "ticketing", "main_module": "servicenow_connector.py", - "app_version": "2.5.0", + "app_version": "2.6.0", "fips_compliant": true, - "min_phantom_version": "6.1.0", + "min_phantom_version": "6.2.1", "python_version": "3", "latest_tested_versions": [ - "Cloud, Build tag: glide-tokyo-07-08-2022__patch1-09-01-2022" + "Cloud, glide-vancouver-07-06-2023__patch2-hotfix1-10-04-2023" ], "product_name": "ServiceNow", "product_version_regex": ".*", @@ -472,7 +472,7 @@ "data_path": "action_result.data.*.display_name", "data_type": "string", "example_values": [ - "SW000077 - Microsoft ASP.NET 2011" + "SW000077 Test" ] }, { @@ -607,7 +607,7 @@ "data_path": "action_result.data.*.model.display_value", "data_type": "string", "example_values": [ - "Microsoft ASP.NET 2011" + "ASP.NET 2011" ] }, { @@ -1040,7 +1040,7 @@ "data_path": "action_result.data.*.vendor.display_value", "data_type": "string", "example_values": [ - "Microsoft" + "PRB000050 Test" ] }, { @@ -6927,7 +6927,7 @@ { "data_path": "action_result.parameter.description", "example_values": [ - "Investigative actions to check for the presence of phapp_servicenow" + "Switch occasionally drops connections" ], "data_type": "string" }, @@ -11559,8 +11559,7 @@ { "data_path": "action_result.summary.total_tickets", "example_values": [ - 2, - 68 + 2 ], "data_type": "numeric" }, @@ -11574,8 +11573,7 @@ { "data_path": "summary.total_objects", "example_values": [ - 1, - 2 + 1 ], "data_type": "numeric" }, @@ -11739,7 +11737,7 @@ "column_order": 3, "column_name": "Email", "example_values": [ - "herman@phantom.us" + "abc@pqr.us" ], "contains": [ "email" @@ -12060,6 +12058,413 @@ "title": "Query Users" } }, + { + "action": "search sources", + "description": "Search for records across multiple tables", + "type": "investigate", + "identifier": "search_sources", + "verbose": "To find the list of search source IDs for the sysparm_search_sources parameter, follow this path in servicenow UI: All > Workspace Experience > Administration > Search Sources. Once there, click with two fingers/right click on the source name and copy the sys_id.", + "read_only": true, + "parameters": { + "sysparm_term": { + "description": "Search record for the given term", + "data_type": "string", + "required": true, + "order": 0 + }, + "sysparm_search_sources": { + "description": "SYS ID of search sources, Comma-separated list allowed", + "data_type": "string", + "required": true, + "order": 1, + "contains": [ + "servicenow ticket sysid" + ], + "primary": true + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.parameter.sysparm_search_sources", + "data_type": "string", + "example_values": [ + "tec681c771testfedf0bcbe2c2606c" + ], + "contains": [ + "servicenow ticket sysid" + ], + "primary": true + }, + { + "data_path": "action_result.parameter.sysparm_term", + "data_type": "string", + "example_values": [ + "Resolved" + ] + }, + { + "data_path": "action_result.data.*.result_count", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.search_results.*.fields.*.label", + "data_type": "string", + "example_values": [ + "Number" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.fields.*.label_plural", + "data_type": "string", + "example_values": [ + "Numbers" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.fields.*.max_length", + "data_type": "numeric", + "example_values": [ + 40 + ] + }, + { + "data_path": "action_result.data.*.search_results.*.fields.*.name", + "data_type": "string", + "example_values": [ + "number" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.fields.*.reference", + "data_type": "string", + "example_values": [ + "sys_user_group" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.fields.*.type", + "data_type": "string", + "example_values": [ + "string" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.label", + "data_type": "string", + "example_values": [ + "Problem" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.limit", + "data_type": "numeric", + "example_values": [ + 20 + ] + }, + { + "data_path": "action_result.data.*.search_results.*.page", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "action_result.data.*.search_results.*.query", + "data_type": "string", + "example_values": [ + "123TEXTQUEtest=Fix Applied" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.record_count", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.assignment_group.display", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.assignment_group.value", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.caller_id.display", + "data_type": "string", + "example_values": [ + "System Administrator" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.caller_id.value", + "data_type": "string", + "example_values": [ + "6816f7test016401c5a33be04be441" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.category.display", + "data_type": "string", + "example_values": [ + "Inquiry / Help" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.category.value", + "data_type": "string", + "example_values": [ + "inquiry" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.cmdb_ci.display", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.cmdb_ci.value", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.number.display", + "data_type": "string", + "example_values": [ + "INC000001" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.number.value", + "data_type": "string", + "example_values": [ + "INC000001" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.opened_at.display", + "data_type": "string", + "example_values": [ + "2023-04-05 00:59:28" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.opened_at.value", + "data_type": "string", + "example_values": [ + "2023-04-05 07:59:28" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.priority.display", + "data_type": "string", + "example_values": [ + "5 - Planning" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.priority.value", + "data_type": "string", + "example_values": [ + "5" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.related_incidents.display", + "data_type": "string", + "example_values": [ + "0" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.related_incidents.value", + "data_type": "string", + "example_values": [ + "0" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.resolution_code.value", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.state.display", + "data_type": "string", + "example_values": [ + "New" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.state.value", + "data_type": "string", + "example_values": [ + "1" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.sys_id.display", + "data_type": "string", + "example_values": [ + "c673edctest1106401f1e3f153af11" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.data.sys_id.value", + "data_type": "string", + "example_values": [ + "td673test1106401f1e3f153af11" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.metadata.description", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.metadata.thumbnail_url", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.metadata.title", + "data_type": "string", + "example_values": [ + "hello" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.record_class_name", + "data_type": "string", + "example_values": [ + "incident" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.record_url", + "data_type": "string", + "example_values": [ + "/incident.do?sys_id=test978221106401f1e99f11&sysparm_view=text_search" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.sys_id", + "data_type": "string", + "example_values": [ + "c673ettest97822119953af11" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.records.*.table", + "data_type": "string", + "example_values": [ + "incident" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.sys_id", + "data_type": "string", + "example_values": [ + "test897862996401f1e3f1990e" + ] + }, + { + "data_path": "action_result.data.*.search_results.*.term", + "data_type": "string", + "example_values": [ + "Resolved" + ] + }, + { + "data_path": "action_result.data.*.sources.*.condition.display", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.sources.*.condition.value", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.sources.*.name.display", + "data_type": "string", + "example_values": [ + "Tasks-Tickets " + ] + }, + { + "data_path": "action_result.data.*.sources.*.name.value", + "data_type": "string", + "example_values": [ + "Tasks-Tickets" + ] + }, + { + "data_path": "action_result.data.*.sources.*.source_table", + "data_type": "string", + "example_values": [ + "Problem" + ] + }, + { + "data_path": "action_result.data.*.sources.*.sys_id", + "data_type": "string", + "example_values": [ + "test8699964099f153af0e99" + ], + "contains": [ + "servicenow ticket sysid" + ], + "primary": true + }, + { + "data_path": "action_result.data.*.term", + "data_type": "string", + "example_values": [ + "Resolved" + ] + }, + { + "data_path": "action_result.summary", + "data_type": "string" + }, + { + "data_path": "action_result.summary.total_records", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Total records: 0" + ] + }, + { + "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, + "height": 5, + "type": "json", + "title": "Search Sources" + }, + "versions": "EQ(*)" + }, { "action": "on poll", "description": "Ingest tickets from SNOW", @@ -12106,10 +12511,6 @@ ], "pip_dependencies": { "wheel": [ - { - "module": "beautifulsoup4", - "input_file": "wheels/py3/beautifulsoup4-4.9.1-py3-none-any.whl" - }, { "module": "python_magic", "input_file": "wheels/shared/python_magic-0.4.18-py2.py3-none-any.whl" @@ -12117,19 +12518,11 @@ { "module": "pytz", "input_file": "wheels/shared/pytz-2021.1-py2.py3-none-any.whl" - }, - { - "module": "soupsieve", - "input_file": "wheels/py3/soupsieve-2.3.2.post1-py3-none-any.whl" } ] }, "pip39_dependencies": { "wheel": [ - { - "module": "beautifulsoup4", - "input_file": "wheels/py3/beautifulsoup4-4.9.1-py3-none-any.whl" - }, { "module": "python_magic", "input_file": "wheels/shared/python_magic-0.4.18-py2.py3-none-any.whl" @@ -12137,10 +12530,6 @@ { "module": "pytz", "input_file": "wheels/shared/pytz-2021.1-py2.py3-none-any.whl" - }, - { - "module": "soupsieve", - "input_file": "wheels/py3/soupsieve-2.5-py3-none-any.whl" } ] } diff --git a/servicenow_connector.py b/servicenow_connector.py index e376dfb..0872281 100644 --- a/servicenow_connector.py +++ b/servicenow_connector.py @@ -1,6 +1,6 @@ # File: servicenow_connector.py # -# Copyright (c) 2016-2023 Splunk Inc. +# Copyright (c) 2016-2024 Splunk Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -68,6 +68,7 @@ class ServicenowConnector(BaseConnector): ACTION_ID_ON_POLL = "on_poll" ACTION_ID_RUN_QUERY = "run_query" ACTION_ID_QUERY_USERS = "query_users" + ACTION_ID_SEARCH_SOURCES = "search_sources" def csv_to_list(self, data): """Comma separated values to list""" @@ -84,6 +85,7 @@ def __init__(self): self._try_oauth = False self._use_token = False self._state = {} + self._response_headers = {} def encrypt_state(self, encrypt_var, token_name): """ Handle encryption of token. @@ -416,7 +418,7 @@ def _make_rest_call_oauth(self, action_result, headers={}, data={}): request_url = '{}{}'.format(self._base_url, '/oauth_token.do') r = requests.post( # nosemgrep request_url, - data=data # Mostly this line + data=data ) except Exception as e: error_message = self._get_error_message_from_exception(e) @@ -452,6 +454,7 @@ def _make_rest_call(self, action_result, endpoint, headers=None, params=None, da return (action_result.set_status(phantom.APP_ERROR, SERVICENOW_ERROR_SERVER_CONNECTION.format(error_message=error_message)), resp_json) + self._response_headers = r.headers return self._process_response(r, action_result) def _make_rest_call_helper(self, action_result, endpoint, params={}, data={}, headers={}, method="get", auth=None): @@ -654,8 +657,8 @@ def _test_connectivity(self, param): self.save_progress(SERVICENOW_ERROR_CONNECTIVITY_TEST) return action_result.set_status(phantom.APP_ERROR) - self.save_progress(SERVICENOW_SUCC_CONNECTIVITY_TEST) - return action_result.set_status(phantom.APP_SUCCESS, SERVICENOW_SUCC_CONNECTIVITY_TEST) + self.save_progress(SERVICENOW_SUCCESS_CONNECTIVITY_TEST) + return action_result.set_status(phantom.APP_SUCCESS, SERVICENOW_SUCCESS_CONNECTIVITY_TEST) def _get_fields(self, param, action_result): @@ -1039,20 +1042,28 @@ def _paginator(self, endpoint, action_result, payload=None, limit=None): if phantom.is_fail(ret_val): return None - if not items.get("result"): - return items_list - items_list.extend(items.get("result")) + # get total record count from headers + if self._response_headers: + total_item_count = int(self._response_headers.get("X-Total-Count")) + + # if result is found + if items.get("result"): + items_list.extend(items.get("result")) + # extend item list if data is present on that page if limit and len(items_list) >= limit: return items_list[:limit] - if len(items.get("result")) < SERVICENOW_DEFAULT_LIMIT: - break + if total_item_count <= limit: + if total_item_count <= SERVICENOW_DEFAULT_LIMIT: + return items_list - payload['sysparm_offset'] += SERVICENOW_DEFAULT_LIMIT - payload['sysparm_limit'] = min(limit - len(items_list), SERVICENOW_DEFAULT_LIMIT) + # exit if the total number of records are less than limit or else it has fetched all the pages + if ((payload["sysparm_offset"] + payload["sysparm_limit"]) == total_item_count): + return items_list - return items_list + payload['sysparm_offset'] += payload['sysparm_limit'] + payload['sysparm_limit'] = min(total_item_count - payload["sysparm_offset"], SERVICENOW_DEFAULT_LIMIT) def _describe_service_catalog(self, param): @@ -1653,6 +1664,83 @@ def _query_users(self, param): return result + def _search_sources_details(self, action_result, sysparm_term, sysparm_search_sources): + + ret_val, auth, headers = self._get_authorization_credentials(action_result) + if phantom.is_fail(ret_val): + return action_result.set_status(phantom.APP_ERROR, SERVICENOW_AUTH_ERROR_MESSAGE) + + params = {"sysparm_term": sysparm_term, "sysparm_search_sources": sysparm_search_sources} + params['sysparm_page'] = SERVICENOW_DEFAULT_PAGE + params['sysparm_limit'] = SERVICENOW_MAX_LIMIT + + items_list = [] + result_length = 0 + first_call = True + total_result_count_page_limit = 0 + + while True: + ret_val, response = self._make_rest_call_helper( + action_result, SERVICENOW_SEARCH_SOURCE_ENDPOINT, auth=auth, headers=headers, params=params + ) + if phantom.is_fail(ret_val): + self.debug_print(action_result.get_message()) + return action_result.set_status(phantom.APP_ERROR, action_result.get_message()) + + total_item_count = int(response.get("result", {}).get("result_count", 0)) + search_results_len = len(response.get("result").get("search_results", [])) + for i in range(search_results_len): + response.get("result").get("search_results", [])[i].pop("limit") + response.get("result").get("search_results", [])[i].pop("page") + result_length += len(response.get("result").get("search_results", [])[i].get("records", [])) + + # Initially fetch response['result'] and extend records into it in subsequent calls + # Add total pages to iterate in the first call to handle empty records due to ACLs + # ServiceNow returns up to 20 records per page by default + if first_call: + items_list.append(response['result']) + total_result_count_page_limit = total_item_count // 20 + first_call = False + else: + for i in range(search_results_len): + data = response.get("result").get("search_results", [])[i].get("records", []) + items_list[0].get('search_results', [])[i].get("records", []).extend(data) + + # If we got all the results or if we reached maximum pages + if total_item_count <= result_length or params['sysparm_page'] >= total_result_count_page_limit + 1: + break + params['sysparm_page'] = params['sysparm_page'] + 1 + + action_result.add_data(items_list) + action_result.update_summary({SERVICENOW_JSON_TOTAL_RECORDS: total_item_count}) + return phantom.APP_SUCCESS + + def _search_sources(self, param): + + action_result = self.add_action_result(ActionResult(dict(param))) + + # Progress + self.save_progress(SERVICENOW_USING_BASE_URL, base_url=self._base_url) + + # Connectivity + self.save_progress(phantom.APP_PROG_CONNECTING_TO_ELLIPSES, self._host) + + sysparm_term = param[SERVICENOW_JSON_SYSPARM_TERM] + sysparm_search_sources = param[SERVICENOW_JSON_SYSPARM_SEARCH_SOURCES] + + search_sources = [x.strip() for x in set(sysparm_search_sources.split(",")) if x.strip()] + + if not search_sources: + return action_result.set_status(phantom.APP_ERROR, "Please provide valid inputs for sysparm_search_sources"), None + + sysparm_search_sources = ",".join(search_sources) + ret_val = self._search_sources_details(action_result, sysparm_term, sysparm_search_sources) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + return action_result.set_status(phantom.APP_SUCCESS) + def _on_poll(self, param): URI_REGEX = '[Hh][Tt][Tt][Pp][Ss]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+#]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+' @@ -1967,6 +2055,8 @@ def handle_action(self, param): ret_val = self._update_ticket(param) elif action == self.ACTION_ID_GET_VARIABLES: ret_val = self._get_variables(param) + elif action == self.ACTION_ID_SEARCH_SOURCES: + ret_val = self._search_sources(param) elif action == self.ACTION_ID_ON_POLL: ret_val = self._on_poll(param) elif action == phantom.ACTION_ID_TEST_ASSET_CONNECTIVITY: diff --git a/servicenow_consts.py b/servicenow_consts.py index f9c7073..d611b78 100644 --- a/servicenow_consts.py +++ b/servicenow_consts.py @@ -1,6 +1,6 @@ # File: servicenow_consts.py # -# Copyright (c) 2016-2023 Splunk Inc. +# Copyright (c) 2016-2024 Splunk Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -42,9 +42,12 @@ SERVICENOW_JSON_EXTRACT_IPS = "extract_ips" SERVICENOW_JSON_EXTRACT_HASHES = "extract_hashes" SERVICENOW_JSON_EXTRACT_URLS = "extract_urls" +SERVICENOW_JSON_SYSPARM_TERM = "sysparm_term" +SERVICENOW_JSON_SYSPARM_SEARCH_SOURCES = "sysparm_search_sources" +SERVICENOW_JSON_TOTAL_RECORDS = "total_records" SERVICENOW_ERROR_CONNECTIVITY_TEST = "Test Connectivity Failed" -SERVICENOW_SUCC_CONNECTIVITY_TEST = "Test Connectivity Passed" +SERVICENOW_SUCCESS_CONNECTIVITY_TEST = "Test Connectivity Passed" SERVICENOW_ERROR_SERVER_CONNECTION = "Connection failed. {error_message}" SERVICENOW_VALIDATE_INTEGER_MESSAGE = "Please provide a valid integer value in the {param} parameter" SERVICENOW_ERROR_FETCH_VALUE = ('Error occurred while fetching variable value' @@ -53,8 +56,7 @@ ' the item_option_value: {item_opt_value} of the System ID: {sys_id}') SERVICENOW_ERROR_FETCH_QUESTION = ('Error occurred while fetching question for' ' the question ID: {question_id} and the item_option_value: {item_opt_value} of the System ID: {sys_id}') -SERVICENOW_ERROR_FROM_SERVER = "API failed, Status code: {status}, Message: {message}, Detail: {detail}.\ -Please check the asset configuration and rerun the test connectivity." +SERVICENOW_ERROR_FROM_SERVER = "API failed, Status code: {status}, Message: {message}, Detail: {detail}." SERVICENOW_MESSAGE_GET_INCIDENT_TEST = "Querying a single Incident to check credentials" SERVICENOW_ERROR_FIELDS_JSON_PARSE = "Unable to parse the fields parameter into a dictionary" SERVICENOW_ERROR_VARIABLES_JSON_PARSE = "Unable to parse the variables parameter into a dictionary" @@ -86,6 +88,10 @@ SERVICENOW_ITEM_OPT_TABLE = "sc_item_option" SERVICENOW_ITEM_OPT_NEW_TABLE = "item_option_new" +# In search sources we only getting 20 results per page +SERVICENOW_DEFAULT_PAGE = 1 +SERVICENOW_MAX_LIMIT = 20 + SERVICENOW_DEFAULT_OFFSET = 0 SERVICENOW_DEFAULT_LIMIT = 10000 SERVICENOW_DEFAULT_MAX_LIMIT = 100 @@ -115,3 +121,4 @@ SERVICENOW_SC_CAT_ITEMS_ENDPOINT = '/table/sc_cat_item' SERVICENOW_CATALOG_OREDERNOW_ENDPOINT = '/servicecatalog/items/{}/order_now' SERVICENOW_API_ENDPOINT = '/api/now' +SERVICENOW_SEARCH_SOURCE_ENDPOINT = "/search/sources/textsearch" diff --git a/servicenow_create_ticket.html b/servicenow_create_ticket.html index 2e67617..51125eb 100644 --- a/servicenow_create_ticket.html +++ b/servicenow_create_ticket.html @@ -10,7 +10,7 @@ {% block widget_content %}