Skip to content

Commit d31e971

Browse files
committed
first commit
0 parents  commit d31e971

File tree

176 files changed

+32635
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

176 files changed

+32635
-0
lines changed

.gitignore

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
app/lib/dependency_scan/odc-reports/*
2+
app/lib/gitleaks/results/*
3+
app/clone_output/*
4+
app/keys/*
5+
6+
# py ext
7+
*.pyc
8+
9+
# C extensions
10+
*.so
11+
12+
# Packages
13+
*.egg
14+
*.egg-info
15+
dist
16+
build
17+
eggs
18+
parts
19+
bin
20+
var
21+
sdist
22+
develop-eggs
23+
.installed.cfg
24+
__pycache__
25+
.DS_Store
26+
27+
# logs
28+
pip-log.txt
29+
*.log
30+
31+
# Unit test / coverage reports
32+
.coverage
33+
.tox
34+
nosetests.xml
35+
36+
# Translations
37+
*.mo
38+
39+
# Mr Developer
40+
.mr.developer.cfg
41+
.project
42+
.pydevproject
43+
44+
# SQLite databases
45+
*.sqlite
46+
47+
# Virtual environment
48+
venv
49+
50+
# Code Editor
51+
.vscode
52+
53+
# Temp folder
54+
tmp

Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# install base
2+
#FROM ubuntu
3+
FROM python:3.6-slim-buster
4+
5+
# update the operating system:
6+
RUN apt-get update \
7+
&& apt-get install -y bzip2 xz-utils zlib1g libxml2-dev libxslt1-dev libgomp1 python3-pip libpq-dev nano net-tools sudo git dos2unix\
8+
&& apt-get clean \
9+
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
10+
11+
# copy the folder to the container:
12+
ADD . /scan7
13+
14+
# Define working directory:
15+
WORKDIR /scan7
16+
17+
# Install the requirements
18+
RUN pip3 install -r /scan7/requirements.txt
19+
20+
# expose tcp port 5000
21+
#EXPOSE 5000
22+
23+
# default command: run the web server
24+
CMD ["/bin/bash","run.sh"]

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
### Scan7
2+
3+
#### The Problem
4+
There is not a great solution in the Open-Source community for performing license, vulnerability and secret detection in a single platform. You end up having to resort to a bunch of shell scripts or purchasing a commercial tool.
5+
6+
#### What does it do?
7+
Scans private/public code repositories for license, vulnerability and secrets data. Track data overtime in the web console and is ideal for security teams.
8+
9+
#### What is the perfect use case?
10+
The current design is ideal for a Security Assurance team that wishes to run out-of-band scans against their company repo's to track licenses, vulnerabilities and secrets at a code level.
11+
12+
#### Limitations?
13+
+ Not ideal to be placed in the CI/CD flow. There is not a API to start/stop commands but that is on the roadmap
14+
+ Not ideal if you need quick and fast results
15+
16+
#### Roadmap
17+
+ Support for CI/CD
18+
+ Customization for the different scan types
19+
+ Dockerize everything
20+
21+
#### Credits
22+
Scan7 utilizes the following Open-Source tools to perform the scanning functionality:
23+
+ Scancode (https://github.com/nexB/scancode-toolkit)
24+
+ Gitleaks (https://github.com/zricethezav/gitleaks)
25+
+ OWASP Dependency Check (https://jeremylong.github.io/DependencyCheck/)

app/__init__.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
from flask import Flask,request,render_template,jsonify
2+
from flask_sqlalchemy import SQLAlchemy
3+
from flask_user import UserManager
4+
from flask_migrate import Migrate, MigrateCommand
5+
from app.utils.flask_logs import LogSetup
6+
from config import config
7+
from apscheduler.schedulers.background import BackgroundScheduler
8+
9+
db = SQLAlchemy()
10+
logs = LogSetup()
11+
migrate = Migrate()
12+
13+
import app.jobs as schjobs
14+
15+
def create_app(config_name="default"):
16+
app = Flask(__name__)
17+
app.config.from_object(config[config_name])
18+
config[config_name].init_app(app)
19+
20+
db.init_app(app)
21+
logs.init_app(app)
22+
23+
from app.models import User,UserInvitation
24+
app.user_manager = UserManager(app, db, User,UserInvitationClass=UserInvitation)
25+
app.db_manager = app.user_manager.db_manager
26+
27+
from app.main import main as main_blueprint
28+
app.register_blueprint(main_blueprint)
29+
30+
from app.api_v1 import api as api_v1_blueprint
31+
app.register_blueprint(api_v1_blueprint, url_prefix='/api/v1')
32+
33+
# Add all models
34+
all_models = {}
35+
classes, models, table_names = [], [], []
36+
for clazz in db.Model._decl_class_registry.values():
37+
try:
38+
table_names.append(clazz.__tablename__)
39+
classes.append(clazz)
40+
except:
41+
pass
42+
for table in db.metadata.tables.items():
43+
if table[0] in table_names:
44+
all_models[table[0]] = classes[table_names.index(table[0])]
45+
models.append(classes[table_names.index(table[0])])
46+
app.models = all_models
47+
48+
# Setup Flask-Migrate
49+
migrate.init_app(app, db)
50+
51+
from app.models import Tasks
52+
53+
def background_daemon():
54+
with app.app_context(),app.test_request_context():
55+
for task in Tasks().ready_to_run():
56+
if app.config["JOB_DEBUG"]:
57+
task.add_log("Executing job: {}".format(task.name),namespace="jobs",meta={"module":task.module})
58+
task.was_executed()
59+
args = task.args or {}
60+
result = getattr(schjobs,task.module)(task,**args)
61+
if result:
62+
task.healthy = True
63+
else:
64+
task.healthy = False
65+
db.session.commit()
66+
67+
scheduler = BackgroundScheduler()
68+
scheduler.add_job(background_daemon, 'interval', seconds=15, misfire_grace_time=3600)
69+
scheduler.start()
70+
71+
@app.errorhandler(404)
72+
def not_found(e):
73+
return render_template("errors/404.html"),404
74+
75+
@app.errorhandler(500)
76+
def internal_error(e):
77+
return render_template("errors/500.html"),500
78+
79+
@app.errorhandler(401)
80+
def unauthorized(e):
81+
if 'Authorization' in request.headers:
82+
return jsonify({"message":"unauthorized"}),401
83+
return "bad"
84+
85+
@app.errorhandler(400)
86+
def malformed(e):
87+
if 'Authorization' in request.headers:
88+
return jsonify({"message":"malformed request"}),400
89+
return "bad"
90+
91+
@app.errorhandler(403)
92+
def forbidden(e):
93+
if 'Authorization' in request.headers:
94+
return jsonify({"message":"forbidden"}),403
95+
return "bad"
96+
97+
'''
98+
@app.before_request
99+
def before_request():
100+
pass
101+
'''
102+
103+
return app

app/api_v1/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from flask import Blueprint
2+
api = Blueprint('api', __name__)
3+
from . import views,search,graphs

app/api_v1/graphs.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from flask import jsonify, request, current_app
2+
from . import api
3+
from flask_user import current_user, login_required, roles_required, roles_accepted
4+
from app.utils.apex_constants import get_graph
5+
from app.utils.db_helper import DynamicQuery
6+
7+
@api.route('/apex', methods = ['GET'])
8+
@login_required
9+
def get_apex_chart():
10+
graph_type = request.args.get('type', None, type=str)
11+
horizontal = request.args.get('horizontal', False, type=bool)
12+
if not graph_type:
13+
return jsonify({"message":"specify type of graph"})
14+
result = DynamicQuery(
15+
model=request.args.get('model', "file", type=str),
16+
request_args=request.args,
17+
qjson=request.get_json(silent=True)
18+
)
19+
response = result.generate()
20+
if not response:
21+
return jsonify({"message":"no data available"})
22+
23+
try:
24+
if graph_type == "bar":
25+
d = get_graph("multi_bar")
26+
d["xaxis"]["categories"] = response["label"]
27+
series = []
28+
for point in response["data"]:
29+
series.append(int(point))
30+
d["series"] = [{"name":"Count","data":series}]
31+
d["yaxis"]["title"]["text"] = "Count"
32+
d["chart"]["height"] = "{}px".format(request.args.get('height', "250", type=str))
33+
d["plotOptions"]["bar"]["horizontal"] = horizontal
34+
35+
if graph_type == "area":
36+
d = get_graph("simple_line")
37+
series = {"name":"Count","data":[]}
38+
for point in response["data"]:
39+
series["data"].append(int(point))
40+
d["series"] = [series]
41+
d["labels"] = [x.capitalize() for x in response["label"]]
42+
d["chart"]["height"] = "{}px".format(request.args.get('height', "250", type=str))
43+
44+
elif graph_type == "donut":
45+
d = get_graph("donut")
46+
d["labels"] = [x.capitalize() for x in response["label"]]
47+
series = []
48+
for point in response["data"]:
49+
series.append(int(point))
50+
d["series"] = series
51+
d["chart"]["height"] = "{}px".format(request.args.get('height', "250", type=str))
52+
53+
d["title"]["text"] = request.args.get('title', "Add title", type=str).capitalize()
54+
d["theme"] = {"palette":"palette1"}
55+
d.pop("colors",None)
56+
return jsonify(d)
57+
except Exception as e:
58+
current_app.logger.warning("Exception caught while drawing graphs:{}".format(str(e)))
59+
return jsonify({"message":"no data available"})

app/api_v1/search.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from flask import jsonify, request, current_app
2+
from . import api
3+
from flask_user import current_user, login_required, roles_required, roles_accepted
4+
from app.models import *
5+
from app.utils.db_helper import DynamicQuery
6+
7+
@api.route("/search/<string:model>", methods=["GET"])
8+
@login_required
9+
def search_api(model):
10+
'''
11+
API for sqlalchemy database tables
12+
'''
13+
result = DynamicQuery(
14+
model=model,
15+
request_args=request.args,
16+
qjson=request.get_json(silent=True)
17+
)
18+
response = result.generate()
19+
return jsonify(response)

app/api_v1/views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from flask import jsonify, request, current_app
2+
from . import api
3+
from flask_user import current_user, login_required, roles_required, roles_accepted
4+
5+
@api.route('/health', methods=['GET'])
6+
@login_required
7+
def get_health():
8+
return jsonify({"message":"ok"})

app/commands/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .init_db import InitDbCommand
2+

0 commit comments

Comments
 (0)