Skip to content

Commit

Permalink
Merge pull request #30 from Santandersecurityresearch/dev
Browse files Browse the repository at this point in the history
Adds CLI tool
  • Loading branch information
danielcuthbert committed May 24, 2021
2 parents 3987ec1 + 64d9b77 commit 35428a7
Show file tree
Hide file tree
Showing 14 changed files with 374 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
grep -i 'current_version = ' setup.cfg | head -1 | tr -d 'current_version = '
- name: Push changes if PR 'to-branch' is main
if: github.base_ref == 'main'
uses: ad-m/github-push-action@master
uses: ad-m/github-push-action@v0.6.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ github.head_ref }}
111 changes: 111 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.pyc
# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
docs/_static/*
!docs/_static/.keep

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# dotenv
.env

# virtualenv
.venv
venv/
ENV/
.DS_Store

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# jetbrains
.idea/

# junit reports folder
reports
62 changes: 53 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,16 @@ Often, CORS configurations make use of wildcards, for example accepting anything

# How Do I Install It?

This project was developed with Python 3.9, but should work with any Python 3.x version.

corsair_scan has been designed to be used as a Python module , so the easiest way to install it is using pip.
This project was developed with Python 3.9, but should work with any Python 3.x version. The best way to install it is using pip.

`pip3 install corsair_scan --user`


# How Do I Use It?

At the moment, corsair_scan is intended to be used as a Python package. However, we plan to release this as a command line tool (CLI) in future releases.
Corsair can be used both as a python module or as a CLI.

### **Python Module**

The method that performs the CORS scan is corsair_scan. Here is its definition:

Expand Down Expand Up @@ -88,10 +88,10 @@ Receives a list of requests and a parameter to enable/disable certificate check



# Example
### Example

```
import corsair_scan
import corsair_scan.corsair_scan as corsair
url_data = {}
data = []
verb = 'GET'
Expand All @@ -108,7 +108,7 @@ url_data['params'] = params
url_data['headers'] = headers
data.append(url_data)
print (corsair_scan.corsair_scan(data, verify=True))
print (corsair.corsair_scan(data, verify=True))
```


Expand Down Expand Up @@ -154,12 +154,56 @@ Response:



### **CLI**

As part of the pip package installation, a CLI is installed. The syntax to execute is as follows:

`corsair FILE [-nv/--noverify][-r/--report]`

The CLI needs a json file with a list of requests perform the scan. An example file is provided [as part of the tests](test/testfiles/json_test.json)

:

```
[{
"verb": "GET",
"url": "https://example.com/",
"params": "user=user1&password=1234",
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-GB,en;q=0.5",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Origin": "https://example.com",
"Host": "example.com"
}
}]
```



There are also two optional parameters:

- -nv / --noverify: To skip certificate validation, if you are testing against a site with a self-signed certificate
- -r / --report: Saves the report as a JSON file



### Example



<img align="left" src="images/cli-report.png" alt="CLI report" style="zoom:80%;" />





## Roadmap

* Release corsair_scan as a CLI tool
* Read url data from a text file
* Release corsair_scan as a CLI tool
* Read url data from a text file
* Improve reports format

# Who Is Behind It?
Expand Down
55 changes: 55 additions & 0 deletions corsair_scan/corsair_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

##################################################
# Santander UK Security Engineering team.
##################################################
# MIT License Copyright (c) 2021 Grupo Santander
##################################################
# Author: Javier Dominguez Ruiz (@javixeneize)
# Version: 1.0
##################################################

import corsair_scan.corsair_scan as corsair
import click
import json
import sys


def get_data_from_file(filename):
try:
with open(filename) as f:
try:
data = json.loads(f.read())
if type(data) == dict:
datalist = []
datalist.append(data)
return datalist
else:
return data
except json.decoder.JSONDecodeError:
print('Error. The format does not appear to be correct, please review')
sys.exit(2)
except FileNotFoundError:
print('Error. File not found')
sys.exit(2)


@click.command()
@click.argument('file', required=True)
@click.option('-nv', '--noverify', 'verify', help='Skips security certificate check', is_flag=True, default=True)
@click.option('-r', '--report', 'report', help='Saves the report in a JSON file')
def run_cli_scan(file, verify, report):
"""Corsair CLI requires as parameter a file in JSON format with the data to run the scan.
This data can be a single request, or a list of requests"""
data = get_data_from_file(file)
corsair_report = corsair.corsair_scan(data, verify)
if corsair_report.get('report'):
if report:
with open(report, 'w') as file:
file.write(json.dumps(corsair_report))
print("Report generated in {}".format(report))
else:
print(corsair_report)
else:
print("There was an error running corsair. Please check the input data is correct")
13 changes: 8 additions & 5 deletions corsair_scan/corsair_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@ def corsair_scan(data: list, verify: bool = True) -> dict:
final_report: dict = {}
for url in data:
single_report: dict = corsair_scan_single_url(url, verify)
full_report.append(single_report.copy())
if single_report:
full_report.append(single_report.copy())
final_report['summary'] = filter_report(full_report)
final_report['report'] = full_report
return final_report


def corsair_scan_single_url(url_data: dict, verify: bool = True) -> dict:
report: dict = {'url': url_data.get('url'), 'verb': url_data.get('verb')}
if validate_data(url_data.get('url'), url_data.get('verb')):
report: dict = {}
if validate_data(url_data.get('url'), url_data.get('verb'), url_data.get('headers')):
report = {'url': url_data.get('url'), 'verb': url_data.get('verb')}
report['fake_origin'] = validate_response(url_data, SM_ORIGIN, verify)
if url_data.get('headers').get('Origin'):
parsed_url = urlparse(url_data.get('headers').get('Origin'))
Expand Down Expand Up @@ -104,5 +106,6 @@ def filter_report(report_list: list) -> dict:
return (final_report)


def validate_data(url, verb):
return validators.url(url) and verb.lower() in VERBS
def validate_data(url, verb, headers):
if url and verb and headers:
return validators.url(url) and verb.lower() in VERBS
Binary file added images/cli-report.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
validators>=0.14.3
requests>=2.23.0
urllib3>=1.25.9
tldextract>=2.2.3
urllib3>=1.26.4
tldextract>=2.2.3
click>=7.1.2
18 changes: 10 additions & 8 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
validators>=0.14.3
requests>=2.23.0
urllib3>=1.25.9
urllib3>=1.26.4
mock>=4.0.2
pytest==3.8.2
pytest-cov==2.5.1
pytest==6.2.4
pytest-cov==2.12.0
wheel
tldextract>=2.2.3
flake8==3.7.9
bandit==1.6.2
safety==1.8.7
bump2version==1.0.0
twine==3.1.1
flake8==3.9.2
bandit==1.7.0
safety==1.10.3
bump2version==1.0.1
twine==3.4.1
click>=7.1.2

6 changes: 3 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.0
current_version = 0.2.0
commit = True
tag = False

Expand All @@ -8,8 +8,8 @@ search = version='{current_version}'
replace = version='{new_version}'

[bumpversion:file:setup.cfg]
search = current_version = '{current_version}'
replace = current_version = '{new_version}'
search = current_version = {current_version}
replace = current_version = {new_version}

[bdist_wheel]
universal = 1
Expand Down

1 comment on commit 35428a7

@fortify-commit-scan
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No weaknesses detected by Fortify Commit Scan in the changed file(s) - keep up the secure coding!

And be sure to incorporate a comprehensive Fortify Static scan (covering 800+ vulnerability categories and advanced detection algorithms) and a Fortify Dynamic scan into your CI/CD pipeline.

Please sign in to comment.