Skip to content

Commit

Permalink
Align credentials stores part 13 (#27289)
Browse files Browse the repository at this point in the history
* Align credentials stores for all Cortex Marketplace integrations- part 13

* adding Tenable_io

* fix

* Update Packs/Tenable_io/Integrations/Tenable_io/Tenable_io.py

Co-authored-by: Shelly Tzohar <45915502+Shellyber@users.noreply.github.com>

* docker update

* added tests

* try

* adding print

* try fix

* removed fixed

* over intended

* added unit tests

* docker image

* headers

* raise DemistoExceptio

* update

---------

Co-authored-by: Shelly Tzohar <45915502+Shellyber@users.noreply.github.com>
  • Loading branch information
maimorag and Shellyber committed Jun 22, 2023
1 parent 0d72afc commit 5cd9536
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 53 deletions.
3 changes: 0 additions & 3 deletions Packs/Pwned/.pack-ignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
[file:PwnedV2.yml]
ignore=IN145

[file:PwnedV2_image.png]
ignore=IM111
82 changes: 45 additions & 37 deletions Packs/Pwned/Integrations/PwnedV2/PwnedV2.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,25 @@

import re
import requests
import urllib3

# Disable insecure warnings
requests.packages.urllib3.disable_warnings()

''' GLOBALS/PARAMS '''
urllib3.disable_warnings()

params = demisto.params()
VENDOR = 'Have I Been Pwned? V2'
MAX_RETRY_ALLOWED = demisto.params().get('max_retry_time', -1)
API_KEY = demisto.params().get('api_key')
USE_SSL = not demisto.params().get('insecure', False)

MAX_RETRY_ALLOWED = params.get('max_retry_time', -1)
API_KEY = params.get('credentials_api_key', {}).get('password') or params.get('api_key')
USE_SSL = not params.get('insecure', False)
BASE_URL = 'https://haveibeenpwned.com/api/v3'
HEADERS = {
'hibp-api-key': API_KEY,
'user-agent': 'DBOT-API',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
DEFAULT_DBOT_SCORE_EMAIL = 2 if params.get('default_dbot_score_email') == 'SUSPICIOUS' else 3
DEFAULT_DBOT_SCORE_DOMAIN = 2 if params.get('default_dbot_score_domain') == 'SUSPICIOUS' else 3


DEFAULT_DBOT_SCORE_EMAIL = 2 if demisto.params().get('default_dbot_score_email') == 'SUSPICIOUS' else 3
DEFAULT_DBOT_SCORE_DOMAIN = 2 if demisto.params().get('default_dbot_score_domain') == 'SUSPICIOUS' else 3
''' GLOBALS/PARAMS '''


VENDOR = 'Have I Been Pwned? V2'
SUFFIXES = {
"email": '/breachedaccount/',
"domain": '/breaches?domain=',
Expand All @@ -49,7 +46,12 @@ def http_request(method, url_suffix, params=None, data=None):
verify=USE_SSL,
params=params,
data=data,
headers=HEADERS
headers={
'hibp-api-key': API_KEY,
'user-agent': 'DBOT-API',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
)

if res.status_code != 429:
Expand All @@ -64,7 +66,7 @@ def http_request(method, url_suffix, params=None, data=None):
wait_amount = wait_regex.group()
else:
demisto.error('failed extracting wait time will use default (5). Res body: {}'.format(res.text))
wait_amount = 5
wait_amount = '5'
if datetime.now() + timedelta(seconds=int(wait_amount)) > RETRIES_END_TIME:
return_error('Max retry time has exceeded.')
time.sleep(int(wait_amount))
Expand Down Expand Up @@ -329,25 +331,31 @@ def pwned_username(username_list):
return api_res_list


command = demisto.command()
LOG('Command being called is: {}'.format(command))
try:
handle_proxy()
set_retry_end_time()
commands = {
'test-module': test_module,
'email': pwned_email_command,
'pwned-email': pwned_email_command,
'domain': pwned_domain_command,
'pwned-domain': pwned_domain_command,
'pwned-username': pwned_username_command
}
def main():
if not API_KEY:
raise DemistoException('API key must be provided.')
command = demisto.command()
LOG(f'Command being called is: {command}')
try:
handle_proxy()
set_retry_end_time()
commands = {
'test-module': test_module,
'email': pwned_email_command,
'pwned-email': pwned_email_command,
'domain': pwned_domain_command,
'pwned-domain': pwned_domain_command,
'pwned-username': pwned_username_command
}
if command in commands:
md_list, ec_list, api_email_res_list = commands[command](demisto.args())
for md, ec, api_paste_res in zip(md_list, ec_list, api_email_res_list):
return_outputs(md, ec, api_paste_res)

# Log exceptions
except Exception as e:
return_error(str(e))

if command in commands:
md_list, ec_list, api_email_res_list = commands[command](demisto.args())
for md, ec, api_paste_res in zip(md_list, ec_list, api_email_res_list):
return_outputs(md, ec, api_paste_res)

# Log exceptions
except Exception as e:
return_error(str(e))
if __name__ in ('__main__', '__builtin__', 'builtins'):
main()
10 changes: 8 additions & 2 deletions Packs/Pwned/Integrations/PwnedV2/PwnedV2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ commonfields:
configuration:
- display: API Key
name: api_key
required: true
required: false
type: 4
hidden: true
- displaypassword: API Key
name: credentials_api_key
required: false
hiddenusername: true
type: 9
- defaultvalue: '30'
display: Maximum time per request (in seconds)
name: max_retry_time
Expand Down Expand Up @@ -254,7 +260,7 @@ script:
runonce: false
script: '-'
subtype: python3
dockerimage: demisto/python3:3.10.6.33415
dockerimage: demisto/python3:3.10.12.63474
type: python
tests:
- Pwned v2 test
Expand Down
96 changes: 94 additions & 2 deletions Packs/Pwned/Integrations/PwnedV2/PwnedV2_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from PwnedV2 import pwned_domain_command, pwned_username_command
from PwnedV2 import pwned_domain_command, pwned_username_command, pwned_email_command
import PwnedV2
from requests_mock import ANY
import demistomock as demisto
Expand Down Expand Up @@ -108,7 +108,10 @@ def test_pwned_commands(command, args, response, expected_result, mocker):
- create the context
validate the expected_result and the created context
"""
mocker.patch.object(demisto, 'params', return_value={'integrationReliability': 'A - Completely reliable'})
PwnedV2.API_KEY = 'test'
mocker.patch.object(demisto, 'params', return_value={
'integrationReliability': 'A - Completely reliable',
'credentials_api_key': {"password": "test"}})
mocker.patch('PwnedV2.http_request', return_value=response)
md_list, ec_list, api_email_res_list = command(args)
for hr, outputs, raw in zip(md_list, ec_list, api_email_res_list):
Expand All @@ -119,12 +122,101 @@ def test_rate_limited(mocker, requests_mock):
# mock all requests with retry and provide a huge timeout
requests_mock.get(ANY, status_code=429,
text='{ "statusCode": 429, "message": "Rate limit is exceeded. Try again in 20 seconds." }')
mocker.patch.object(demisto, 'params', return_value={'credentials_api_key': {'password': 'test'}})
return_error_mock = mocker.patch(RETURN_ERROR_TARGET)
return_error_mock.side_effect = ValueError(RETURN_ERROR_TARGET)
PwnedV2.MAX_RETRY_ALLOWED = 10
PwnedV2.API_KEY = 'test'
PwnedV2.set_retry_end_time()
with pytest.raises(ValueError, match=RETURN_ERROR_TARGET):
PwnedV2.pwned_email(['test@test.com'])
assert return_error_mock.call_count == 1
# call_args last call with a tuple of args list and kwargs
assert 'Max retry time' in return_error_mock.call_args[0][0]


def test_valid_emails(mocker):
"""
Given:
- A list of valid email addresses.
When:
- Calling the pwned_email_command function.
Then:
- Ensure the function returns the expected output.
"""
email_list = ['test1@example.com', 'test2@example.com']
api_email_res_list = [{'Title': 'Breach1', 'Domain': 'example.com', 'PwnCount': 100, 'IsVerified': True,
'BreachDate': '2021-01-01T00:00:00Z', 'Description': '<p>Breach description</p>',
'DataClasses': ['Emails', 'Passwords']}, None]
api_paste_res_list = [[{'Source': 'Paste1', 'Title': 'Paste Title', 'Id': '1234', 'Date': '2021-01-01T00:00:00Z',
'EmailCount': 10}], []]
expected_md_list = [
'### Have I Been Pwned query for email: *test1@example.com*\n'
'#### Breach1 (example.com): 100 records breached [Verified breach]\n'
'Date: **2021-01-01**\n\n'
'Breach description\n'
'Data breached: **Emails,Passwords**\n'
'\n'
'The email address was found in the following "Pastes":\n'
'| ID | Title | Date | Source | Amount of emails in paste |\n'
'|----|-------|------|--------|--------------------------|\n'
'| 1234 | Paste Title | 2021-01-01 | Paste1 | 10 |\n',
'### Have I Been Pwned query for email: *test2@example.com*\n'
'No records found'
]
expected_ec_list = [
{
'DBotScore': {
'Indicator': 'test1@example.com',
'Type': 'email',
'Vendor': 'HaveIBeenPwned',
'Score': 3,
'Reliability': 'B - Usually reliable'
},
'email': {
'Address': 'test1@example.com',
'Pwned-V2': {
'Compromised': {
'Vendor': 'HaveIBeenPwned',
'Reporters': 'Breach1, Paste1'
}
},
'Malicious': {
'Vendor': 'HaveIBeenPwned',
'Description': 'The email has been compromised'
}
}
},
{
'DBotScore': {
'Indicator': 'test2@example.com',
'Type': 'email',
'Vendor': 'HaveIBeenPwned',
'Score': 0,
'Reliability': 'B - Usually reliable'
},
'email': {
'Address': 'test2@example.com',
'Pwned-V2': {
'Compromised': {
'Vendor': 'HaveIBeenPwned',
'Reporters': ''
}
}
}
}
]

mocker.patch.object(demisto, 'params', return_value={'integrationReliability': 'B - Usually reliable'})
mocker.patch.object(demisto, 'command', return_value='pwned-email')
mocker.patch.object(demisto, 'args', return_value={'email': email_list})
mocker.patch('PwnedV2.pwned_email', return_value=(api_email_res_list, api_paste_res_list))
mocker.patch('PwnedV2.data_to_markdown', side_effect=expected_md_list)
mocker.patch('PwnedV2.email_to_entry_context', side_effect=expected_ec_list)

md_list, ec_list, api_paste_res = pwned_email_command(demisto.args())

assert md_list == expected_md_list
assert ec_list == expected_ec_list
6 changes: 6 additions & 0 deletions Packs/Pwned/ReleaseNotes/1_0_10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

#### Integrations

##### Have I Been Pwned? v2
- Added the *API Key* integration parameters to support credentials fetching object.
- Updated the Docker image to: *demisto/python3:3.10.12.63474*.
2 changes: 1 addition & 1 deletion Packs/Pwned/pack_metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "Pwned",
"description": "Uses the Have I Been Pwned? service to check whether email addresses, domains, or usernames were compromised in previous breaches.",
"support": "xsoar",
"currentVersion": "1.0.9",
"currentVersion": "1.0.10",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
Expand Down
2 changes: 1 addition & 1 deletion Packs/Tenable_io/.pack-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
ignore=RM104

[file:Tenable_io.yml]
ignore=BA108,BA109,IN145
ignore=BA108,BA109

[file:TenableioEventCollector_1_3.yml]
ignore=MR108
Expand Down
6 changes: 4 additions & 2 deletions Packs/Tenable_io/Integrations/Tenable_io/Tenable_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@
'Critical']

BASE_URL = demisto.params()['url']
ACCESS_KEY = demisto.params()['access-key']
SECRET_KEY = demisto.params()['secret-key']
ACCESS_KEY = demisto.params().get('credentials_access_key', {}).get('password') or demisto.params()['access-key']
SECRET_KEY = demisto.params().get('credentials_secret_key', {}).get('password') or demisto.params()['secret-key']
USER_AGENT_HEADERS_VALUE = 'Integration/1.0 (PAN; Cortex-XSOAR; Build/2.0)'
AUTH_HEADERS = {'X-ApiKeys': f"accessKey={ACCESS_KEY}; secretKey={SECRET_KEY}"}
NEW_HEADERS = {
Expand Down Expand Up @@ -1115,6 +1115,8 @@ def export_vulnerabilities_command(args: Dict[str, Any]) -> PollResult:


def main(): # pragma: no cover
if not (ACCESS_KEY and SECRET_KEY):
raise DemistoException('Access Key and Secret Key must be provided.')
if demisto.command() == 'test-module':
demisto.results(test_module())
elif demisto.command() == 'tenable-io-list-scans':
Expand Down
18 changes: 15 additions & 3 deletions Packs/Tenable_io/Integrations/Tenable_io/Tenable_io.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,24 @@ configuration:
type: 0
- display: Access key
name: access-key
required: true
required: false
type: 4
hidden: true
- displaypassword: Access key
name: credentials_access_key
required: false
hiddenusername: true
type: 9
- display: Secret key
name: secret-key
required: true
required: false
type: 4
hidden: true
- displaypassword: secret key
name: credentials_secret_key
required: false
hiddenusername: true
type: 9
- display: Trust any certificate (not secure)
name: unsecure
required: false
Expand Down Expand Up @@ -1210,7 +1222,7 @@ script:
script: '-'
subtype: python3
type: python
dockerimage: demisto/python3:3.10.10.48392
dockerimage: demisto/python3:3.10.12.63474
tests:
- Tenable.io test
fromversion: 5.0.0
6 changes: 6 additions & 0 deletions Packs/Tenable_io/ReleaseNotes/2_1_7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

#### Integrations

##### Tenable.io
- Added the *Access key* and *Secret key* integration parameters to support credentials fetching object.
- Updated the Docker image to: *demisto/python3:3.10.12.63474*.
4 changes: 2 additions & 2 deletions Packs/Tenable_io/pack_metadata.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "Tenable.io",
"description": "A comprehensive asset centric solution to accurately track\u00a0resources while accommodating\u00a0dynamic assets such as cloud, mobile devices, containers and web applications.",
"description": "A comprehensive asset centric solution to accurately track resources while accommodating dynamic assets such as cloud, mobile devices, containers and web applications.",
"support": "xsoar",
"currentVersion": "2.1.6",
"currentVersion": "2.1.7",
"author": "Cortex XSOAR",
"url": "https://www.paloaltonetworks.com/cortex",
"email": "",
Expand Down

0 comments on commit 5cd9536

Please sign in to comment.