Skip to content

Commit

Permalink
Merge pull request #22 from bentleygd/patch/v0.1.1
Browse files Browse the repository at this point in the history
Documentation updates and bug fixes.
  • Loading branch information
bentleygd authored Nov 25, 2024
2 parents a300683 + 9023c29 commit 2190291
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 104 deletions.
27 changes: 13 additions & 14 deletions .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
name: Lint
name: Lint and Test

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
branches:
- master

jobs:
lint:
Lint:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
- uses: actions/checkout@v1
- name: Set up Python 3.11.10
uses: actions/setup-python@v1
with:
python-version: 3.8
python-version: 3.11.10
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
python -m pip install -r requirements.txt
# Installing testing dependencies
python -m pip install flake8 bandit
- name: Lint with flake8
run: |
pip install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. PEP8 max line length is 79
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=79 --statistics
- name: Security lint with bandit
- name: Test with bandit
run: |
pip install bandit
bandit -ll . --recursive
bandit -ll --recursive .
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Python scripts that utilize Trend Micro Cloud App Security APIs for phishing identification, mitigation and remediation. As a courtesy warning, if Trend Micro ever substantially changes their APIs this code may not work.

[![Total alerts](https://img.shields.io/lgtm/alerts/g/bentleygd/orca.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/bentleygd/orca/alerts/) [![Language grade: Python](https://img.shields.io/lgtm/grade/python/g/bentleygd/orca.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/bentleygd/orca/context:python) [![Known Vulnerabilities](https://snyk.io/test/github/bentleygd/orca/badge.svg)](https://snyk.io/test/github/bentleygd/orca)
[![Known Vulnerabilities](https://snyk.io/test/github/bentleygd/orca/badge.svg)](https://snyk.io/test/github/bentleygd/orca) ![Lint with Bandit and Flake8](https://github.com/bentleygd/ITGC/workflows/Lint/badge.svg) ![CodeQL](https://github.com/bentleygd/CSIC/workflows/CodeQL/badge.svg)

## Purpose
The purpose of orca is to automate finding and removing phishing emails for customers of Trend Micro Cloud App Security. The utilization of orca can significantally reduce man hours spent on containing and eradicating phishing threats. The CLI client can also be leveraged to "deputize" teams outside core security teams (such as the help desk) so that phishing threats can be addressed as soon as users report them to the help desk instead of having to wait for the extra minutes needed to notify the security team.
Expand Down
86 changes: 52 additions & 34 deletions cli_client.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
"""
This module is a CLI script that calls the class/functions in orca.py and
coreutils.py. This is the 'main' script that is executed by usres to find
and remove phishing emails.
Classes:
None
Functions:
None
"""
from argparse import ArgumentParser
from configparser import ConfigParser
from logging import basicConfig, DEBUG, getLogger
Expand Down Expand Up @@ -64,46 +75,36 @@
# Looking for phishing emails based on supplied arguments.
# Check if URL is supplied.
if orca_args.url is not None:
# Performing input validation.
url_validate = validate.URL(orca_args.url)
if url_validate is False:
print('Input validation for URL failed. Exiting')
exit(1)
# Finding and pulling emails with indicated URL.
phish_list = phish_hunt.find_phish(url=orca_args.url)
print('*' * 32 + 'WARNING' + '*' * 32)
print('You are going to pull email from %d mailboxes.' % len(phish_list))
print('You are going to pull email from %d mailboxes.', len(phish_list))
warning = str(input('Press Y/N to continue> '))
if warning.lower() == 'y':
log.info('Acknowledgment accepted for %d mailboxes' % len(phish_list))
log.info('Acknowledgment accepted for %d mailboxes', len(phish_list))
else:
print('*' * 32 + 'ABORTING' + '*' * 32)
exit(0)
# Checking for pull or purge and taking appropriate action.
log.debug('Performing URL pull for %s' % orca_args.url)
log.debug('Performing URL pull for %s', orca_args.url)
if orca_args.action == 'pull':
phish_hunt.pull_email(phish_list)
# elif orca_args.action == 'purge':
# phish_hunt.purge_email(phish_list)
# Check if file hash is supplied.
elif orca_args.hash is not None:
# Performing input validation.
hash_validate = validate.SHA1(orca_args.hash)
if hash_validate is False:
print('SHA1 hash failed input validation. Exiting.')
exit(1)
# Finding and pulling emails with indicated file hash.
phish_list = phish_hunt.find_phish(file_hash=orca_args.hash)
print('*' * 32 + 'WARNING' + '*' * 32)
print('You are going to pull email from %d mailboxes.' % len(phish_list))
print('You are going to pull email from %d mailboxes.', len(phish_list))
warning = str(input('Press Y/N to continue> '))
if warning.lower() == 'y':
log.info('Acknowledgment accepted for %d mailboxes' % len(phish_list))
log.info('Acknowledgment accepted for %d mailboxes', len(phish_list))
else:
print('*' * 32 + 'ABORTING' + '*' * 32)
exit(0)
# Checking for pull or purge and taking appropriate action.
log.debug('Performing SHA1 hash pull for %s' % orca_args.hash)
log.debug('Performing SHA1 hash pull for %s', orca_args.hash)
if orca_args.action == 'pull':
phish_hunt.pull_email(phish_list)
# elif orca_args.action == 'purge':
Expand All @@ -115,11 +116,11 @@
orca_args.file_extension is not None
):
# Beginning input validation.
validate_sender = validate.Email(orca_args.sender)
validate_sender = validate.email(orca_args.sender)
if validate_sender is False:
print('Sender email address faield input validation. Exiting.')
exit(1)
validate_file = validate.FileExt(orca_args.file_extension)
validate_file = validate.file_ext(orca_args.file_extension)
if validate_file is False:
print('File extension input validation failed. Exiting.')
exit(1)
Expand All @@ -130,10 +131,10 @@
file_ext=orca_args.file_extension
)
print('*' * 32 + 'WARNING' + '*' * 32)
print('You are going to pull email from %d mailboxes.' % len(phish_list))
print('You are going to pull email from %d mailboxes.', len(phish_list))
warning = str(input('Press Y/N to continue> '))
if warning.lower() == 'y':
log.info('Acknowledgment accepted for %d mailboxes' % len(phish_list))
log.info('Acknowledgment accepted for %d mailboxes', len(phish_list))
else:
print('*' * 32 + 'ABORTING' + '*' * 32)
exit(0)
Expand All @@ -152,7 +153,7 @@
elif (orca_args.sender is not None and
orca_args.subject is not None):
# Performing input validation.
sender_validate = validate.Email(orca_args.sender)
sender_validate = validate.email(orca_args.sender)
if sender_validate is False:
print('Sender email address failed validation. Exiting.')
exit(1)
Expand All @@ -161,10 +162,10 @@
subject=orca_args.subject
)
print('*' * 32 + 'WARNING' + '*' * 32)
print('You are going to pull email from %d mailboxes.' % len(phish_list))
print('You are going to pull email from %d mailboxes.', len(phish_list))
warning = str(input('Press Y/N to continue> '))
if warning.lower() == 'y':
log.info('Acknowledgment accepted for %d mailboxes' % len(phish_list))
log.info('Acknowledgment accepted for %d mailboxes', len(phish_list))
else:
print('*' * 32 + 'ABORTING' + '*' * 32)
exit(0)
Expand All @@ -182,11 +183,11 @@
elif (orca_args.sender is not None and
orca_args.file_extension is not None):
# Performing input validation.
sender_validate = validate.Email(orca_args.sender)
sender_validate = validate.email(orca_args.sender)
if sender_validate is False:
print('Sender email address failed input validation. Exiting.')
exit(1)
file_validate = validate.FileExt(orca_args.file_extension)
file_validate = validate.file_ext(orca_args.file_extension)
if file_validate is False:
print('File extension input validation failed. Exiting.')
exit(1)
Expand All @@ -196,10 +197,10 @@
file_ext=orca_args.file_extension
)
print('*' * 32 + 'WARNING' + '*' * 32)
print('You are going to pull email from %d mailboxes.' % len(phish_list))
print('You are going to pull email from %d mailboxes.', len(phish_list))
warning = str(input('Press Y/N to continue> '))
if warning.lower() == 'y':
log.info('Acknowledgment accepted for %d mailboxes' % len(phish_list))
log.info('Acknowledgment accepted for %d mailboxes', len(phish_list))
else:
print('*' * 32 + 'ABORTING' + '*' * 32)
exit(0)
Expand All @@ -216,22 +217,39 @@
# Checking only for sender
elif orca_args.sender is not None:
# Performing input validation.
validate_sender = validate.Email(orca_args.sender)
if validate_sender is False:
print('Sender email address failed input validation. Exiting.')
exit(1)
# validate_sender = validate.Email(orca_args.sender)
# if validate_sender is False:
# print('Sender email address failed input validation. Exiting.')
# exit(1)
# Finding and pulling emails that match the sender.
phish_list = phish_hunt.find_phish(sender=orca_args.sender)
print('*' * 32 + 'WARNING' + '*' * 32)
print('You are going to pull email from %d mailboxes.' % len(phish_list))
print('You are going to pull email from %d mailboxes.', len(phish_list))
warning = str(input('Press Y/N to continue> '))
if warning.lower() == 'y':
log.info('Acknowledgment accepted for %d mailboxes', len(phish_list))
else:
print('*' * 32 + 'ABORTING' + '*' * 32)
exit(0)
# Checking for pull or purge and taking appropriate action.
log.debug('Pulling email based on sender: %s', orca_args.sender)
if orca_args.action == 'pull':
phish_hunt.pull_email(phish_list)
# elif orca_args.action == 'purge':
# phish_hunt.purge_email(phish_list)
# Chcking for subject only
elif orca_args.subject is not None:
phish_list = phish_hunt.find_phish(subject=orca_args.subject)
print('*' * 32 + 'WARNING' + '*' * 32)
print('You are going to pull email from %d mailboxes.', len(phish_list))
warning = str(input('Press Y/N to continue> '))
if warning.lower() == 'y':
log.info('Acknowledgment accepted for %d mailboxes' % len(phish_list))
log.info('Acknowledgment accepted for %d mailboxes', len(phish_list))
else:
print('*' * 32 + 'ABORTING' + '*' * 32)
exit(0)
# Checking for pull or purge and taking appropriate action.
log.debug('Pulling email based on sender: %s' % orca_args.sender)
log.debug('Pulling email based on sender: %s', orca_args.sender)
if orca_args.action == 'pull':
phish_hunt.pull_email(phish_list)
# elif orca_args.action == 'purge':
Expand Down
50 changes: 21 additions & 29 deletions libs/coreutils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
#!/usr/bin/python3
"""
This module provides basic functions and methods meant to be used by other
modules.
Functions:
mail_send - sends email via SMTP.
get_credentials - retrieves credentials from an encrypted password file.
Classes:
ValidateInput - performs input validation.
"""
from socket import gethostbyname, gaierror
from smtplib import SMTP, SMTPConnectError
from email.mime.text import MIMEText
Expand Down Expand Up @@ -34,11 +44,11 @@ def mail_send(mail_info):
try:
s = SMTP(gethostbyname(mail_info['server']), '25')
except gaierror:
print('Hostname resolution of %s failed.' % mail_info['server'])
print('Hostname resolution of %s failed.', mail_info['server'])
exit(1)
except SMTPConnectError:
print('Unable to connect to %s, the server refused the ' +
'connection.' % mail_info['server'])
'connection.', mail_info['server'])
exit(1)
# Sending the mail.
s.sendmail(mail_info['sender'], mail_info['recipients'], msg.as_string())
Expand Down Expand Up @@ -70,7 +80,7 @@ def get_credentials(scss_dict):
# Connecting to SCSS. If SSL verification fails, change verify to
# false. This isn't recommended (as it defeats the purpose of
# verification), but it will make the code work in an emergency.
scss_response = post(url, headers=headers)
scss_response = post(url, headers=headers, timeout=5)
try:
scss_response.raise_for_status
except HTTPError:
Expand All @@ -85,35 +95,17 @@ def get_credentials(scss_dict):


class ValidateInput:
"""Performs input validation."""
def __init__(self):
"""Input validation class
Methods:
URL - Input validation for a URL.
SHA1 - Input validation for a SHA1 hash.
Email - Input validation for a email address.
FileExt - Input validation for a file extension.
Subject - Input validation for email subject line."""

def URL(self, url):
"""Input validation for a URL.
Input:
url - str(), The supplied URL to validate.
Returns:
Boolean - The method will return True if input validation
passes or False if input validation fails."""
url_pattern = (
r'(http:|https:)\/\/(\w+\.\w+|\w+\.\w+\.\w+|\w+\.\w+\.\w+\.\w+)\/\S+'
)
url_validate = match(url_pattern, url)
if url_validate:
return True
else:
return False

def Email(self, email):
def email(self, email):
"""Input validation for an email address.
Input:
Expand All @@ -131,7 +123,7 @@ def Email(self, email):
else:
return False

def SHA1(self, hash):
def sha1(self, _hash):
"""Input validation for a SHA1 hash.
Input:
Expand All @@ -140,14 +132,14 @@ def SHA1(self, hash):
Returns:
Boolean - The method will return True if input validation
passes or False if input validation fails."""
hash_pattern = r'[a-z0-9]{40}'
hash_validate = match(hash_pattern, hash)
hash_pattern = r'[a-zA-Z0-9]{40}'
hash_validate = match(hash_pattern, _hash)
if hash_validate:
return True
else:
return False

def FileExt(self, file_ext):
def file_ext(self, file_ext):
"""Input validation for a file extension.
Input:
Expand All @@ -163,7 +155,7 @@ def FileExt(self, file_ext):
else:
return False

def Subject(self, subject_line):
def subject(self, subject_line):
"""Input validation for an email subject line.
Yes, it isn't a whole lot.
Expand Down
Loading

0 comments on commit 2190291

Please sign in to comment.