Compare commits

...

21 Commits

Author SHA1 Message Date
lgtm-com[bot] d8d0c8a6cc
Merge 7b050c956d into 664c2f8a90 2024-03-03 02:38:59 -07:00
David Cruciani 664c2f8a90
fix: [website] default admin password 2024-02-29 10:34:53 +01:00
David Cruciani 4426a51193
chg: [website] admin user 2024-02-29 10:15:16 +01:00
David Cruciani 7a31404c7b
fix: [website] readme images 2024-02-28 08:53:02 +01:00
David Cruciani 3372ee508e
chg: [website] screen for misp-module 2024-02-27 11:21:49 +01:00
David Cruciani 2b94f73c15
fix: [website] print and css 2024-02-27 11:21:29 +01:00
David Cruciani ee02bb171b
chg: [website] readme 2024-02-27 11:20:46 +01:00
David Cruciani ba25204ffb
chg: [website] width + markdown non misp standard 2024-02-26 15:35:42 +01:00
David Cruciani e737605c1b
fix: [website] misp-objects submodule 2024-02-26 15:24:34 +01:00
David Cruciani a2721c967b
chg: [website] use ui-priority 2024-02-26 14:54:25 +01:00
David Cruciani 50d6e60074
chg: [website] parse result not in misp standard 2024-02-26 14:53:40 +01:00
David Cruciani b5b42e6807
chg: [website] module select
when select 'ip' select 'ip-dst' and 'ip-src' too
2024-02-26 14:52:30 +01:00
David Cruciani 7e3ead7b9e
chg: [website] mardown source, download, result view 2024-02-23 14:54:51 +01:00
David Cruciani 10cf1e4a32
fix: [website] module select 2024-02-23 14:47:25 +01:00
David Cruciani 5a27f3d25d
chg: [website] query with same name 2024-02-22 14:35:47 +01:00
David Cruciani e1ebd89f69
fix: [website] attributes and modules selection 2024-02-22 14:27:55 +01:00
David Cruciani 769f7454e2
chg: [website] delete first node of history tree 2024-02-22 14:03:48 +01:00
David Cruciani 01decb1d44
chg: [website] use join() in js 2024-02-22 11:41:07 +01:00
David Cruciani 03e4f79a76
add: [website] query as same 2024-02-22 11:31:14 +01:00
David Cruciani 59a0131880
add: [website] lib js misp object parser 2024-02-22 11:30:27 +01:00
LGTM Migrator 7b050c956d
Add CodeQL workflow for GitHub code scanning 2022-11-10 20:07:32 +00:00
31 changed files with 986 additions and 336 deletions

42
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: "CodeQL"
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: "54 22 * * 2"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python, javascript ]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
- name: Autobuild
uses: github/codeql-action/autobuild@v2
if: ${{ matrix.language == 'python' || matrix.language == 'javascript' }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"

View File

@ -1,3 +1,48 @@
# MISP-module website
Use all modules with a dedicate website without any MISP
![home](https://github.com/MISP/misp-modules/blob/main/website/doc/home_misp_module.png?raw=true)
![query](https://github.com/MISP/misp-modules/blob/main/website/doc/query_misp_module.png?raw=true)
## Installation
**It is strongly recommended to use a virtual environment**
If you want to know more about virtual environments, [python has you covered](https://docs.python.org/3/tutorial/venv.html)
```bash
sudo apt-get install screen -y
pip install -r requirements.txt
git submodule init && git submodule update ## Initialize misp-objects submodule
python3 app.py -i ## Initialize db
```
## Config
Edit `config.py`
- `SECRET_KEY`: Secret key for the app
- `FLASK_URL` : url for the instance
- `FLASK_PORT`: port for the instance
- `MISP_MODULE`: url and port where misp-module is running
- `ADMIN_USER`: If True, config page will not be accessible
- `ADMIN_PASSWORD`: Password for Admin user if `ADMIN_USER` is True
## Launch
```bash
./launch.sh -l
```
## Admin user
If admin user is active, type `/login` in url to access a login page and type the password wrote in `config.py` in `ADMIN_PASSOWRD`.

View File

@ -4,6 +4,18 @@ from flask import render_template
import os
from app.utils.init_modules import create_modules_db
import signal
import sys
import subprocess
def signal_handler(sig, frame):
path = os.path.join(os.getcwd(), "launch.sh")
req = [path, "-ks"]
subprocess.call(req)
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--init_db", help="Initialise the db if it not exist", action="store_true")

View File

@ -3,6 +3,7 @@ from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect
from flask_migrate import Migrate
from flask_session import Session
from flask_login import LoginManager
from config import config as Config
import os
@ -11,7 +12,8 @@ import os
db = SQLAlchemy()
csrf = CSRFProtect()
migrate = Migrate()
sess = Session()
session = Session()
login_manager = LoginManager()
def create_app():
app = Flask(__name__)
@ -25,12 +27,16 @@ def create_app():
csrf.init_app(app)
migrate.init_app(app, db, render_as_batch=True)
app.config["SESSION_SQLALCHEMY"] = db
sess.init_app(app)
session.init_app(app)
login_manager.login_view = "account.login"
login_manager.init_app(app)
from .home import home_blueprint
from .history.history import history_blueprint
from .account.account import account_blueprint
app.register_blueprint(home_blueprint, url_prefix="/")
app.register_blueprint(history_blueprint, url_prefix="/")
app.register_blueprint(account_blueprint, url_prefix="/")
return app

View File

@ -0,0 +1,45 @@
from ..db_class.db import User
from flask import Blueprint, render_template, redirect, url_for, request, flash
from .form import LoginForm
from flask_login import (
login_required,
login_user,
logout_user,
current_user
)
from ..utils.utils import admin_password
from ..db_class.db import User
from .. import db
account_blueprint = Blueprint(
'account',
__name__,
template_folder='templates',
static_folder='static'
)
@account_blueprint.route('/login', methods=['GET', 'POST'])
def login():
"""Log in an existing user."""
form = LoginForm()
if form.validate_on_submit():
if form.password.data == str(admin_password()):
user = User(email="admin@admin.admin")
db.session.add(user)
db.session.commit()
login_user(user, form.remember_me.data)
flash('You are now logged in. Welcome back!', 'success')
return redirect(request.args.get('next') or "/")
else:
flash('Invalid password.', 'error')
return render_template('account/login.html', form=form)
@account_blueprint.route('/logout')
@login_required
def logout():
User.query.filter_by(id=current_user.id).delete()
logout_user()
flash('You have been logged out.', 'info')
return redirect(url_for('home.home'))

View File

@ -0,0 +1,13 @@
from flask_wtf import FlaskForm
from wtforms.fields import (
BooleanField,
PasswordField,
SubmitField
)
from wtforms.validators import InputRequired
class LoginForm(FlaskForm):
password = PasswordField('Password', validators=[InputRequired()])
remember_me = BooleanField('Keep me logged in')
submit = SubmitField('Log in')

View File

@ -1,5 +1,6 @@
import json
from .. import db
from .. import db, login_manager
from flask_login import UserMixin, AnonymousUserMixin
class Module(db.Model):
@ -76,3 +77,30 @@ class Module_Config(db.Model):
config_id = db.Column(db.Integer, index=True)
value = db.Column(db.String, index=True)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
first_name = db.Column(db.String(64), index=True)
last_name = db.Column(db.String(64), index=True)
email = db.Column(db.String(64), unique=True, index=True)
def to_json(self):
return {
"id": self.id,
"first_name": self.first_name,
"last_name": self.last_name,
"email": self.email
}
class AnonymousUser(AnonymousUserMixin):
def is_admin(self):
return False
def read_only(self):
return True
login_manager.anonymous_user = AnonymousUser
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

View File

@ -1,6 +1,7 @@
import json
from flask import Flask, Blueprint, render_template, request, jsonify
from flask import Flask, Blueprint, render_template, request, jsonify, session as sess
from . import history_core as HistoryModel
from ..utils.utils import admin_user_active
history_blueprint = Blueprint(
'history',
@ -13,6 +14,7 @@ history_blueprint = Blueprint(
@history_blueprint.route("/history", methods=["GET"])
def history():
"""View all history"""
sess["admin_user"] = admin_user_active()
return render_template("history.html")
@history_blueprint.route("/get_history", methods=["GET"])
@ -25,6 +27,7 @@ def get_history():
@history_blueprint.route("/history_session", methods=["GET"])
def history_session():
"""View all history"""
sess["admin_user"] = admin_user_active()
return render_template("history_session.html", tree_view=False)
@history_blueprint.route("/get_history_session", methods=["GET"])
@ -49,6 +52,7 @@ def save_history(sid):
@history_blueprint.route("/history_tree", methods=["GET"])
def history_tree():
"""View all history"""
sess["admin_user"] = admin_user_active()
return render_template("history_session.html", tree_view=True)
@history_blueprint.route("/get_history_tree", methods=["GET"])

View File

@ -134,13 +134,15 @@ def get_history_tree():
def get_history_tree_uuid(history_uuid):
history_tree = History_Tree.query.filter_by(session_uuid=history_uuid).first()
tree = json.loads(history_tree.tree)
loc_session = get_session(history_tree.session_uuid)
loc_json = loc_session.history_json()
loc_json["children"] = list()
for child in tree[history_tree.session_uuid]:
loc_json["children"].append(util_get_history_tree(child))
return loc_json
if history_tree:
tree = json.loads(history_tree.tree)
loc_session = get_session(history_tree.session_uuid)
loc_json = loc_session.history_json()
loc_json["children"] = list()
for child in tree[history_tree.session_uuid]:
loc_json["children"].append(util_get_history_tree(child))
return loc_json
return {}
def util_remove_node_session(node_uuid, parent, parent_path):
@ -153,14 +155,19 @@ def util_remove_node_session(node_uuid, parent, parent_path):
return util_remove_node_session(node_uuid, child, parent_path["children"][i])
def remove_node_session(node_uuid):
for q in sess:
if isUUID(q):
q_value = sess.get(q)
keys_list = list(sess.keys())
loc = None
for i in range(0, len(keys_list)):
if isUUID(keys_list[i]):
q_value = sess.get(keys_list[i])
if q_value["uuid"] == node_uuid:
del sess[q]
loc = i
break
else:
if q_value["children"]:
return util_remove_node_session(node_uuid, q_value, sess[q])
return util_remove_node_session(node_uuid, q_value, sess[keys_list[i]])
if loc:
del sess[keys_list[i]]
@ -181,8 +188,7 @@ def remove_node_tree(node_uuid):
tree = json.loads(history_tree.tree)
for e in tree:
if e == node_uuid:
del tree[e]
history_tree.tree = json.dumps(tree)
db.session.delete(history_tree)
db.session.commit()
return
else:

View File

@ -1,7 +1,9 @@
import json
from flask import Flask, Blueprint, render_template, request, jsonify
from flask import Blueprint, render_template, request, jsonify, session as sess
from flask_login import current_user
from . import session_class as SessionModel
from . import home_core as HomeModel
from . import session as SessionModel
from .utils.utils import admin_user_active
home_blueprint = Blueprint(
'home',
@ -13,10 +15,14 @@ home_blueprint = Blueprint(
@home_blueprint.route("/")
def home():
sess["admin_user"] = admin_user_active()
if "query" in request.args:
return render_template("home.html", query=request.args.get("query"))
return render_template("home.html")
@home_blueprint.route("/home/<sid>", methods=["GET", "POST"])
def home_query(sid):
sess["admin_user"] = admin_user_active()
if "query" in request.args:
query = request.args.get("query")
return render_template("home.html", query=query, sid=sid)
@ -24,6 +30,7 @@ def home_query(sid):
@home_blueprint.route("/query/<sid>")
def query(sid):
sess["admin_user"] = admin_user_active()
session = HomeModel.get_session(sid)
flag=False
if session:
@ -46,6 +53,31 @@ def query(sid):
@home_blueprint.route("/get_query_info/<sid>")
def get_query_info(sid):
"""Return info for a query"""
session = HomeModel.get_session(sid)
flag=False
if session:
flag = True
query_loc = session.query_enter
else:
for s in SessionModel.sessions:
if s.uuid == sid:
flag = True
query_loc = s.query
session=s
if flag:
loc_dict = {
"query": query_loc,
"input_query": session.input_query,
"modules": json.loads(session.modules_list),
"query_date": session.query_date.strftime('%Y-%m-%d %H:%M')
}
return loc_dict
return {"message": "Session not found"}, 404
@home_blueprint.route("/get_modules")
def get_modules():
"""Return all modules available"""
@ -68,9 +100,12 @@ def get_list_misp_attributes():
def run_modules():
"""Run modules"""
if "query" in request.json:
if "input" in request.json:
if "input" in request.json and request.json["input"]:
if "modules" in request.json:
session = SessionModel.Session_class(request.json)
if "query_as_same" in request.json:
session = SessionModel.Session_class(request.json, query_as_same=True, parent_id=request.json["parent_id"])
else:
session = SessionModel.Session_class(request.json)
HomeModel.set_flask_session(session, request.json["parent_id"])
session.start()
SessionModel.sessions.append(session)
@ -105,40 +140,74 @@ def result(sid):
@home_blueprint.route("/download/<sid>")
def download(sid):
"""Download a module result as json"""
sess = HomeModel.get_session(sid)
if "module" in request.args:
if sess:
loc = json.loads(sess.result)
module = request.args.get("module")
if module in loc:
return jsonify(loc[module]), 200, {'Content-Disposition': f'attachment; filename={sess.query_enter.replace(".", "_")}-{module}.json'}
return {"message": "Module not in result", "toast_class": "danger-subtle"}, 400
else:
for s in SessionModel.sessions:
if s.uuid == sid:
module = request.args.get("module")
if module in s.result:
return jsonify(s.result[module]), 200, {'Content-Disposition': f'attachment; filename={s.query}-{module}.json'}
return {"message": "Module not in result", "toast_class": "danger-subtle"}, 400
return {"message": "Session not found", 'toast_class': "danger-subtle"}, 404
return {"message": "Need to pass a module", "toast_class": "warning-subtle"}, 400
@home_blueprint.route("/modules_config")
def modules_config():
"""List all modules for configuration"""
return render_template("modules_config.html")
sess["admin_user"] = admin_user_active()
if sess.get("admin_user"):
if current_user.is_authenticated:
return render_template("modules_config.html")
return render_template("404.html")
@home_blueprint.route("/modules_config_data")
def modules_config_data():
"""List all modules for configuration"""
modules_config = HomeModel.get_modules_config()
return modules_config, 200
sess["admin_user"] = admin_user_active()
if sess.get("admin_user"):
if current_user.is_authenticated:
modules_config = HomeModel.get_modules_config()
return modules_config, 200
return {"message": "Permission denied"}, 403
@home_blueprint.route("/change_config", methods=["POST"])
def change_config():
"""Change configuation for a module"""
if "module_name" in request.json["result_dict"]:
res = HomeModel.change_config_core(request.json["result_dict"])
if res:
return {'message': 'Config changed', 'toast_class': "success-subtle"}, 200
return {'message': 'Something went wrong', 'toast_class': "danger-subtle"}, 400
return {'message': 'Need to pass "module_name"', 'toast_class': "warning-subtle"}, 400
sess["admin_user"] = admin_user_active()
if sess.get("admin_user"):
if current_user.is_authenticated:
if "module_name" in request.json["result_dict"]:
res = HomeModel.change_config_core(request.json["result_dict"])
if res:
return {'message': 'Config changed', 'toast_class': "success-subtle"}, 200
return {'message': 'Something went wrong', 'toast_class': "danger-subtle"}, 400
return {'message': 'Need to pass "module_name"', 'toast_class': "warning-subtle"}, 400
return {'message': 'Permission denied', 'toast_class': "danger-subtle"}, 403
@home_blueprint.route("/change_status", methods=["GET"])
def change_status():
"""Change the status of a module, active or unactive"""
if "module_id" in request.args:
res = HomeModel.change_status_core(request.args.get("module_id"))
if res:
return {'message': 'Module status changed', 'toast_class': "success-subtle"}, 200
return {'message': 'Something went wrong', 'toast_class': "danger-subtle"}, 400
return {'message': 'Need to pass "module_id"', 'toast_class': "warning-subtle"}, 400
sess["admin_user"] = admin_user_active()
if sess.get("admin_user"):
if current_user.is_authenticated:
if "module_id" in request.args:
res = HomeModel.change_status_core(request.args.get("module_id"))
if res:
return {'message': 'Module status changed', 'toast_class': "success-subtle"}, 200
return {'message': 'Something went wrong', 'toast_class': "danger-subtle"}, 400
return {'message': 'Need to pass "module_id"', 'toast_class': "warning-subtle"}, 400
return {'message': 'Permission denied', 'toast_class': "danger-subtle"}, 403

View File

@ -1,5 +1,5 @@
import json
from .utils.utils import query_get_module, isUUID
from .utils.utils import query_get_module
from . import db
from .db_class.db import History, Module, Config, Module_Config, Session_db, History_Tree
from flask import session as sess
@ -148,6 +148,29 @@ def get_history():
def create_new_session_tree(current_session, parent_id):
loc_session = get_session(parent_id)
loc_json = {
"uuid": loc_session.uuid,
"modules": json.loads(loc_session.modules_list),
"query": loc_session.query_enter,
"input": loc_session.input_query,
"query_date": loc_session.query_date.strftime('%Y-%m-%d %H:%M'),
"config": json.loads(loc_session.config_module)
}
loc_json_child = {
"uuid": current_session.uuid,
"modules": current_session.modules_list,
"query": current_session.query,
"input": current_session.input_query,
"query_date": current_session.query_date.strftime('%Y-%m-%d'),
"config": current_session.config_module,
"children": []
}
sess["current_query"] = loc_session.uuid
sess[sess.get("current_query")] = loc_json
sess[sess.get("current_query")]["children"] = [loc_json_child]
def util_set_flask_session(parent_id, loc_session, current_session):
if parent_id == loc_session["uuid"]:
@ -156,7 +179,8 @@ def util_set_flask_session(parent_id, loc_session, current_session):
"modules": current_session.modules_list,
"query": current_session.query,
"input": current_session.input_query,
"query_date": current_session.query_date.strftime('%Y-%m-%d')
"query_date": current_session.query_date.strftime('%Y-%m-%d %H:%M'),
"config": current_session.config_module
}
loc_session["children"].append(loc_json)
return True
@ -172,23 +196,14 @@ def deep_explore(session_dict, parent_id, current_session):
return False
def set_flask_session(current_session, parent_id):
current_query = sess.get("current_query")
if not current_query or current_query not in sess:
loc_json = {
"uuid": current_session.uuid,
"modules": current_session.modules_list,
"query": current_session.query,
"input": current_session.input_query,
"query_date": current_session.query_date.strftime('%Y-%m-%d')
}
sess["current_query"] = current_session.uuid
sess[sess.get("current_query")] = loc_json
sess[sess.get("current_query")]["children"] = list()
else:
# sess["uuid"]
loc_session = sess.get(sess.get("current_query"))
if not "children" in loc_session:
loc_session["children"] = list()
if not util_set_flask_session(parent_id, loc_session, current_session):
sess["current_query"] = current_session.uuid
if parent_id:
current_query = sess.get("current_query")
if not current_query or current_query not in sess:
create_new_session_tree(current_session, parent_id)
else:
# sess["uuid"]
loc_session = sess.get(sess.get("current_query"))
if not "children" in loc_session:
loc_session["children"] = list()
if not util_set_flask_session(parent_id, loc_session, current_session):
create_new_session_tree(current_session, parent_id)

View File

@ -3,16 +3,17 @@ import json
from queue import Queue
from threading import Thread
from uuid import uuid4
from .utils.utils import query_post_query, query_get_module
from .utils.utils import query_post_query, query_get_module, get_object
from . import home_core as HomeModel
import uuid
from . import db
from .db_class.db import History, History_Tree, Session_db
from flask import session as sess
sessions = list()
class Session_class:
def __init__(self, request_json) -> None:
def __init__(self, request_json, query_as_same=False, parent_id=None) -> None:
self.uuid = str(uuid4())
self.thread_count = 4
self.jobs = Queue(maxsize=0)
@ -24,20 +25,41 @@ class Session_class:
self.input_query = request_json["input"]
self.modules_list = request_json["modules"]
self.nb_errors = 0
self.config_module = self.config_module_setter(request_json)
self.config_module = self.config_module_setter(request_json, query_as_same, parent_id)
self.query_date = datetime.datetime.now(tz=datetime.timezone.utc)
def config_module_setter(self, request_json):
def util_config_as_same(self, child, parent_id):
if child["uuid"] == parent_id:
return child["config"]
elif "children" in child:
for c in child["children"]:
return self.util_config_as_same(c, parent_id)
def config_module_setter(self, request_json, query_as_same, parent_id):
"""Setter for config for all modules used"""
for query in self.modules_list:
if not query in request_json["config"]:
request_json["config"][query] = {}
module = HomeModel.get_module_by_name(query)
mcs = HomeModel.get_module_config_module(module.id)
for mc in mcs:
config_db = HomeModel.get_config(mc.config_id)
request_json["config"][query][config_db.name] = mc.value
flag = False
if query_as_same:
current_query_val = sess.get(sess.get("current_query"))
if current_query_val:
if current_query_val["uuid"] == parent_id:
return current_query_val["config"]
else:
for child in current_query_val["children"]:
res = self.util_config_as_same(child, parent_id)
if res:
flag = True
return res
if not flag:
for query in self.modules_list:
if not query in request_json["config"]:
request_json["config"][query] = {}
module = HomeModel.get_module_by_name(query)
mcs = HomeModel.get_module_config_module(module.id)
for mc in mcs:
config_db = HomeModel.get_config(mc.config_id)
request_json["config"][query][config_db.name] = mc.value
return request_json["config"]
def start(self):
@ -109,6 +131,20 @@ class Session_class:
else:
send_to = {"module": work[1], self.input_query: self.query, "config": loc_config}
res = query_post_query(send_to)
## Sort attr in object by ui-priority
if "results" in res:
if "Object" in res["results"]:
for obj in res["results"]["Object"]:
loc_obj = get_object(obj["name"])
if loc_obj:
for attr in obj["Attribute"]:
attr["ui-priority"] = loc_obj["attributes"][attr["object_relation"]]["ui-priority"]
# After adding 'ui-priority'
obj["Attribute"].sort(key=lambda x: x["ui-priority"], reverse=True)
# print(res)
if "error" in res:
self.nb_errors += 1

View File

@ -6,7 +6,10 @@ body {
background-color: #fbfbfb;
}
main
{
overflow-x: hidden;
}
span#goTop{
position: fixed;
right: 1em;

View File

@ -56,7 +56,7 @@ export default {
</li>
<li class="list-group-item">
<h5 style="color: brown"><u>Modules</u></h5>
<template v-for="module in history.modules">[[module]],</template>
[[history.modules.join(", ")]]
</li>
</ul>
</a>
@ -77,7 +77,7 @@ export default {
<br>
<p class="mb-1" style="color: #2000ff;"><u>Modules</u>:</p>
<div>
<template v-for="module in history.modules">[[module]],</template>
[[history.modules.join(", ")]]
</div>
<div></div>
<div class="d-flex w-100 justify-content-between">

View File

@ -0,0 +1,119 @@
function parseMispObject(misp_object, query_url, functionToCall){
function generate(misp_object, query_url, functionToCall){
let $container = $("<div>")
misp_object.Attribute.forEach(function (v, i) {
let $query = $("<a>")
let $query_same = null
if(v.type != 'counter' && v.type != 'datetime'){
if(query_url){
$query=$("<a>").attr("href", query_url+v.value).text("query").css("margin-left", "10px")
}
// `_${functionToCall.name}('${v.value}')` refer to 'window._query_as_same = query_as_same' in my vue file
$query_same = $("<button>").attr({"onclick": `_${functionToCall.name}('${v.value}')`,
"title": "Query this value with the same attribute and modules as the main query",
"class": "btn btn-link"
})
.text("query as same")
.css({"margin-left": "10px", "padding": "0", "--bs-btn-border-width": "0"})
}
$container.append(
$("<div>").css("margin-top", "10px").append(
$("<h6>").append($("<u>").text(v.object_relation)),
$("<div>").css("display", "flex").append(
$("<div>").css({
"border-left": "2px solid grey",
"border-bottom": "2px solid grey",
"width": "15px",
"height": "15px",
"display": "flex",
"margin-left": "0.5em",
}),
$("<div>").text("Type: "+ v.type)
),
$("<div>").css("display", "flex").append(
$("<div>").css({
"border-left": "2px solid grey",
"border-bottom": "2px solid grey",
"width": "15px",
"height": "15px",
"display": "flex",
"margin-left": "0.5em",
}),
$("<div>").text("Value: "+ v.value),
$query,
$query_same
)
)
)
});
return $container
}
var $mainContainer = $('<div>')
let first_elem = $("<div>").css({"display": "flex", "align-items": "baseline"}).append(
$("<i>").attr({"class": "fa-solid fa-cube", "title": "MISP Object"}),
$('<h4>').css("margin-left", "5px").append(
$("<small>").text(misp_object.name)
)
)
$mainContainer.append(first_elem)
$mainContainer.append(generate(misp_object, query_url, functionToCall))
$mainContainer.append($("<hr>"))
return $mainContainer
}
function parseMispAttr(misp_attr, misp_types, key, query_url, query_as_same){
let $query = $("<a>")
let $query_same = null
if(!misp_types.includes('counter') && !misp_types.includes('datetime') ){
if(query_url){
$query=$("<a>").attr("href", query_url+misp_attr).text("query").css("margin-left", "10px")
}
// `_${functionToCall.name}('${misp_attr}')` refer to 'window._query_as_same = query_as_same' in my vue file
$query_same = $("<button>").attr({"onclick": `_${query_as_same.name}('${misp_attr}')`,
"title": "Query this value with the same attribute and modules as the main query",
"class": "btn btn-link"
})
.text("query as same")
.css({"margin-left": "10px", "padding": "0", "--bs-btn-border-width": "0"})
}
var $mainContainer = $('<div>')
let cp = key+1
$mainContainer.append($("<h6>").append($("<u>").text("#Attr "+cp)))
$mainContainer.append($("<div>").css("display", "flex").append(
$("<div>").css({
"border-left": "2px solid grey",
"border-bottom": "2px solid grey",
"width": "15px",
"height": "15px",
"display": "flex",
"margin-left": "0.5em",
}),
$("<div>").text("Type: "+ misp_types.join(", "))
)
)
$mainContainer.append($("<div>").css("display", "flex").append(
$("<div>").css({
"border-left": "2px solid grey",
"border-bottom": "2px solid grey",
"width": "15px",
"height": "15px",
"display": "flex",
"margin-left": "0.5em",
}),
$("<div>").text("Value: "+ misp_attr),
$query,
$query_same
)
)
$mainContainer.append($("<hr>"))
return $mainContainer
}

View File

@ -0,0 +1,126 @@
<!--
Author: David Cruciani
-->
{% import 'macros/form_macros.html' as f %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Flowintel-cm</title>
<script src="{{ url_for('static',filename='js/popper.min.js') }}"></script>
<script src="{{ url_for('static',filename='bootstrap-5.3.0/js/bootstrap.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/jquery.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/jquery-ui.js') }}"></script>
<script src="{{ url_for('static',filename='js/vue.global.js') }}"></script>
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='bootstrap-5.3.0/css/bootstrap.min.css') }}">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='fontawesome-6.3.0/css/fontawesome.css') }}">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='fontawesome-6.3.0/css/solid.css') }}">
<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='css/jquery-ui.css') }}">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
html, body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
</style>
</head>
<body>
<main class="form-signin">
{% include 'macros/_flashes.html' %}
{% set flashes = {
'error': get_flashed_messages(category_filter=['form-error']),
'warning': get_flashed_messages(category_filter=['form-check-email']),
'info': get_flashed_messages(category_filter=['form-info']),
'success': get_flashed_messages(category_filter=['form-success'])
} %}
<form action="" method="post">
{{ form.hidden_tag() }}
<div class="mb-3">
{{form.password.label}}:
{{form.password(class_="form-control")}}
</div>
<div class="checkbox mb-3">
{{form.remember_me.label}}:
{{form.remember_me}}
</div>
{{ f.form_message(flashes['error'], header='Something went wrong.', class='error') }}
{{ f.form_message(flashes['warning'], header='Check your email.', class='warning') }}
{{ f.form_message(flashes['info'], header='Information', class='info') }}
{{ f.form_message(flashes['success'], header='Success!', class='success') }}
{{form.submit(class='btn btn-primary')}}
</form>
</main>
<!--Main layout-->
{# Implement CSRF protection for site #}
{% if csrf_token()|safe %}
<div style="visibility: hidden; display: none">
<input type="hidden" id="csrf_token" name="csrf_token" value="{{ csrf_token()|safe }}">
</div>
{% endif %}
</body>
<script>
$('.message').each((i, el) => {
const $el = $(el);
const $xx = $el.find('.close');
const sec = $el.data('autohide');
const triggerRemove = () => clearTimeout($el.trigger('remove').T);
$el.one('remove', () => $el.remove());
$xx.one('click', triggerRemove);
if (sec) $el.T = setTimeout(triggerRemove, sec * 1000);
});
</script>
</html>

View File

@ -18,6 +18,7 @@
<script src="{{ url_for('static',filename='js/utils.js') }}"></script>
<script src="{{ url_for('static',filename='js/jsonParser.js') }}"></script>
<script src="{{ url_for('static',filename='js/mispParser.js') }}"></script>
<script src="{{ url_for('static',filename='js/dayjs/dayjs.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/dayjs/dayjs-utc.js') }}"></script>

View File

@ -38,7 +38,7 @@
<br>
<p class="mb-1" style="color: #2000ff;"><u>Modules</u>:</p>
<div>
<template v-for="module in h.modules">[[module]],</template>
[[h.modules.join(", ")]]
</div>

View File

@ -32,10 +32,14 @@
</li>
<li class="list-group-item">
<h5 style="color: brown"><u>Modules</u></h5>
<template v-for="module in his.modules">[[module]],</template>
[[his.modules.join(", ")]]
</li>
</ul>
</a>
<div style="display: flex; align-items: center; margin-left: 3px">
<button v-if="!tree_view" class="btn btn-danger btn-sm" title="Remove this node" @click="remove_node(his, key)"><i class="fa-solid fa-trash"></i></button>
<button v-else class="btn btn-danger btn-sm" title="Remove this node" @click="remove_node_tree(his, key)"><i class="fa-solid fa-trash"></i></button>
</div>
</div>
<div>
<div class="collapse" :id="'collapse'+his.uuid" style="width: 70%; margin-left:30px">
@ -49,7 +53,7 @@
<br>
<p class="mb-1" style="color: #2000ff;"><u>Modules</u>:</p>
<div>
<template v-for="module in his.modules">[[module]],</template>
[[his.modules.join(", ")]]
</div>
<div></div>
<div class="d-flex w-100 justify-content-between">
@ -111,17 +115,35 @@
display_toast(res)
}
async function remove_node(history_loc, key){
const res = await fetch('/history/remove_node_session/' + history_loc.uuid)
display_toast(res)
await change_tree(history_loc, key)
}
async function remove_node_tree(history_loc, key){
const res = await fetch('/history/remove_node_tree/' + history_loc.uuid)
display_toast(res)
await change_tree(history_loc, key)
}
async function change_tree(history_loc, key){
if(!tree_view){
if(!tree_view.value){
const res = await fetch("/get_history_session/"+history_loc.uuid)
let loc = await res.json()
history.value[key] = loc
if(!Object.keys(loc).length){
history.value.splice(key, key+1)
}else{
history.value[key] = loc
}
}else{
const res = await fetch("/get_history_tree/"+history_loc.uuid)
let loc = await res.json()
history.value[key] = loc
}
if(!Object.keys(loc).length){
history.value.splice(key, key+1)
}else{
history.value[key] = loc
}
}
}
onMounted(() => {
@ -142,7 +164,9 @@
history,
tree_view,
save_history,
change_tree
change_tree,
remove_node,
remove_node_tree
}
}
}).mount('.container-fluid')

View File

@ -23,7 +23,7 @@
<input type="hidden" id="parent_id" value="{{sid}}">
<input type="text" value="{{query}}" id="process-query" placeholder="Enter here..." autofocus class="form-control" style="border-radius: 5px;" />
</div>
<span v-if="status_site" id="status">[[status_site]]</span>
<span v-if="status_site" style="color: brown;" id="status">[[status_site]]</span>
</div>
<br>
@ -48,7 +48,7 @@
<h4>Modules</h4>
<select data-placeholder="Modules" class="select2-modules form-control" multiple name="modules_select" id="modules_select">
<template v-for="key in modules_list">
<option v-if="key.mispattributes.input.includes(attr_selected)" :value="key.name" :title="key.meta.description">[[key.name]]</option>
<option v-if="checked_attr(key.mispattributes.input)" :value="key.name" :title="key.meta.description">[[key.name]]</option>
</template>
</select>
</div>
@ -105,8 +105,18 @@
progress.value = 0
let error_flag = false
let result_dict = {"modules": $("#modules_select").val(),
"input": $("#input_select").val(),
let loc = undefined
if($("#input_select").val() != "None"){
loc = $("#input_select").val()
}
let loc_modules = undefined
if($("#modules_select").val().length){
loc_modules = $("#modules_select").val()
}
let result_dict = {"modules": loc_modules,
"input": loc,
"query": current_query.value,
"parent_id": $("#parent_id").val()
}
@ -222,7 +232,14 @@
}
function checked_attr(arr1){
return arr1.includes(attr_selected.value)
let loc = arr1.includes(attr_selected.value)
if(!loc && attr_selected.value == 'ip'){
loc = arr1.includes('ip-dst')
if(!loc && attr_selected.value == 'ip'){
loc = arr1.includes('ip-src')
}
}
return loc
}

View File

@ -41,10 +41,7 @@
<h4>[[ current_config['module_name'] ]]</h4>
<i>
<small>
Attributes:
<template v-for="attr in current_config['input_attr']">
[[ attr ]],
</template>
Attributes: [[current_config['input_attr'].join(", ")]]
</small>
</i>
<template v-for="conf, key in current_config">

View File

@ -11,7 +11,11 @@
<h2>{{query}}</h2>
</div>
<a style="float: right;" class="btn btn-primary" href="/">New query</a>
<div class="btn-group" style="float: right;" role="group" aria-label="Basic mixed styles example">
<a style="float: right;" class="btn btn-primary" href="/" title="Do a new query with no relation with this one">New query</a>
<a style="float: right;" class="btn btn-secondary" href="/?query={{query}}" title="New query with same name">Query</a>
</div>
<div class="card card-body">
<div class="row">
@ -21,7 +25,7 @@
</div>
<div class="col">
<h4>Modules:</h4>
{%for module in modules%} {{module}}, {%endfor%}
{{", ".join(modules)}}
</div>
</div>
<div class="d-flex w-100 justify-content-between">
@ -39,7 +43,7 @@
<br/>
<button class="btn btn-outline-primary" style="position: fixed; right: 0px; top: 50%" title="Session history" data-bs-toggle="offcanvas" data-bs-target="#offcanvasScrolling" aria-controls="offcanvasScrolling">
<button class="btn btn-outline-primary" style="position: fixed; right: 0px; margin-top:30px" title="Session history" data-bs-toggle="offcanvas" data-bs-target="#offcanvasScrolling" aria-controls="offcanvasScrolling">
<i class="fa-solid fa-bars"></i>
</button>
@ -67,22 +71,118 @@
<hr>
<ul class="nav nav-tabs" style="margin-bottom: 10px;">
<li class="nav-item">
<button class="nav-link active" id="tab-json" aria-current="page" @click="active_tab('json')">Json</button>
<button class="nav-link active" id="tab-visual" @click="active_tab('visual')">Visual</button>
</li>
<li class="nav-item">
<button class="nav-link" id="tab-parser" @click="active_tab('parser')">Parser</button>
<button class="nav-link" id="tab-json" aria-current="page" @click="active_tab('json')">Json</button>
</li>
<li class="nav-item">
<button class="nav-link" id="tab-markdown" @click="active_tab('markdown')">Markdown</button>
</li>
</ul>
<template v-if="tab_list == 'json'">
<div class="row" v-if="Object.keys(modules_res).length">
<div class="col-10">
<h3 id="results_part">Results</h3>
<div class="accordion">
<div class="accordion-item" v-for="result, key in modules_res">
<div class="row" style="margin-bottom: 50px;">
<div class="col-10">
<template v-if="tab_list == 'visual'">
<div data-bs-spy="scroll" data-bs-target="#list-result" data-bs-smooth-scroll="true" class="scrollspy-example" tabindex="0">
<div class="accordion" v-if="Object.keys(modules_res).length" style="width: 95%">
<div class="accordion-item" :id="'list-item-'+key" v-for="result, key in modules_res">
<template v-if="!('error' in result)">
<h2 class="accordion-header">
<button class="accordion-button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
[[key]]
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<div class="accordion-body" >
<template v-if="'Object' in result.results">
<template v-for="obj in result.results.Object">
<div v-html="parseMispObject(obj, '/home/{{sid}}?query=', query_as_same)[0].outerHTML"></div>
</template>
</template>
<template v-else>
<!-- <div class="accordion-body" v-html="generateCoreFormatUI(result)[0].outerHTML"></div> -->
<template v-for="misp_attrs, key_loop in result.results">
<div v-for="misp_attr in misp_attrs.values" class="accordion-body" v-html="parseMispAttr(misp_attr, misp_attrs.types, key_loop, '/home/{{sid}}?query=', query_as_same)[0].outerHTML"></div>
</template>
</template>
</div>
</div>
</template>
</div>
</div>
</div>
<!-- Errors Part -->
<hr style="margin-top: 50px; width: 95%">
<h3 id="errors_part">Errors</h3>
<div data-bs-spy="scroll" data-bs-target="#list-error" data-bs-smooth-scroll="true" class="scrollspy-example" tabindex="0">
<div class="accordion" style="width: 95%">
<div class="accordion-item" :id="'list-item-'+key" v-for="result, key in modules_res">
<template v-if="'error' in result">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
[[key]]
<span style="margin-left: 5px;" title="Error"></span>
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<div class="accordion-body" v-html="generateCoreFormatUI(result)[0].outerHTML"></div>
</div>
</template>
</div>
</div>
</div>
</template>
<template v-else-if="tab_list == 'json'">
<div v-if="Object.keys(modules_res).length">
<h3 id="results_part">Results</h3>
<div data-bs-spy="scroll" data-bs-target="#list-result" data-bs-smooth-scroll="true" class="scrollspy-example" tabindex="0">
<div class="accordion" style="width: 95%">
<div class="accordion-item" :id="'list-item-'+key" v-for="result, key in modules_res">
<template v-if="!('error' in result)">
<h2 class="accordion-header">
<button class="accordion-button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
[[key]]
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<a class="btn btn-primary" :href="`/download/${sid}?module=${key}`" title="Download the json" style="padding: 5px; margin-left: 5px; margin-top: 5px;">Download</a>
<div class="accordion-body" v-html="generateCoreFormatUI(result)[0].outerHTML"></div>
</div>
</template>
</div>
</div>
</div>
<!-- Errors Part -->
<hr style="margin-top: 50px; width: 95%">
<h3 id="errors_part">Errors</h3>
<div data-bs-spy="scroll" data-bs-target="#list-error" data-bs-smooth-scroll="true" class="scrollspy-example" tabindex="0">
<div class="accordion" style="width: 95%">
<div class="accordion-item" :id="'list-item-'+key" v-for="result, key in modules_res">
<template v-if="'error' in result">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
[[key]]
<span style="margin-left: 5px;" title="Error"></span>
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<div class="accordion-body" v-html="generateCoreFormatUI(result)[0].outerHTML"></div>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<template v-else-if="tab_list == 'markdown'">
<div v-if="Object.keys(modules_res).length" class="accordion" style="width: 95%">
<div class="accordion-item" :id="'list-item-'+key" v-for="result, key in modules_res">
<template v-if="!('error' in result)">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
@ -90,102 +190,56 @@
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<div class="accordion-body" v-html="generateCoreFormatUI(result)[0].outerHTML"></div>
<div class="accordion-body row">
<template v-if="'Object' in result.results">
<template v-for="obj, key_obj in result.results.Object">
<pre>
#### [[obj.name]]
<template v-for="attr, key_attr in obj.Attribute">
###### [[attr.object_relation]]
Type: [[attr.type]]
Value: [[attr.value]]
</template>
</pre>
<hr>
</template>
</template>
<template v-else>
<template v-for="misp_attrs, key_loop in result.results">
<template v-for="misp_attr in misp_attrs.values">
<pre>
#### Attr [[key_loop +1]]
Type: [[misp_attrs.types.join(", ")]]
Value: [[misp_attr]]
</pre>
</template>
</template>
</template>
</div>
</div>
</template>
</div>
</div>
</template>
</div>
<!-- Errors Part -->
<hr style="margin-top: 50px">
<h3 id="errors_part">Errors</h3>
<div class="accordion">
<div class="accordion-item" v-for="result, key in modules_res">
<template v-if="'error' in result">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
[[key]]
<span style="margin-left: 5px;" title="Error"></span>
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<div class="accordion-body" v-html="generateCoreFormatUI(result)[0].outerHTML"></div>
</div>
</template>
</div>
<div id="list-result" class="list-group col-2" style="position: fixed; right: 0px;">
<a class="list-group-item list-group-item-action" v-if="tab_list == 'json'" style="background-color: #0d6efd; color:white" href="#results_part">Results</a>
<template v-for="result, key in modules_res">
<a class="list-group-item list-group-item-action" v-if="!('error' in result)" :href="'#list-item-'+key">[[key]]</a>
</template>
<template v-if="tab_list == 'json' || tab_list == 'visual'">
<a class="list-group-item list-group-item-action" style="background-color: #0d6efd; color:white" href="#errors_part">Errors</a>
<div id="list-error" class="list-group">
<template v-for="result, key in modules_res">
<a class="list-group-item list-group-item-action" v-if="'error' in result" :href="'#list-item-'+key" style="border-radius: 0;">[[key]]</a>
</template>
</div>
</div>
<div class="col-1" style="position: fixed; right: 0px; box-shadow: 0 2px 5px 0 rgb(0 0 0 / 5%), 0 2px 10px 0 rgb(0 0 0 / 5%);">
<div style="padding: 10px;"><a style="text-decoration: none;" href="#results_part">Results</a></div>
<div style="padding: 10px;"><a style="text-decoration: none;" href="#errors_part">Errors</a></div>
</div>
</div>
</template>
<template v-else-if="tab_list == 'parser'">
<div class="accordion" v-if="Object.keys(modules_res).length" style="width: 95%">
<div class="accordion-item" v-for="result, key in modules_res">
<template v-if="!('error' in result)">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
[[key]]
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<div class="accordion-body row">
<template v-for="obj, key_obj in result.results.Object">
<h4>Object #[[key_obj+1]] - <small>[[obj.name]]</small></h4>
<div class="row" style="margin: 5px; padding: 5px;">
<div class="col-4 mb-3" style="border: 1px solid #939393; border-radius: 10px;" v-for="attr, key_attr in obj.Attribute">
<h6>Attributes #[[key_attr+1]]</h6>
<div>
Type: [[attr.type]]
</div>
<div>
Value: [[attr.value]]
</div>
<a v-if="attr.type != 'counter' && attr.type != 'datetime'" :href="'/home/{{sid}}?query='+attr.value">query</a>
</div>
</div>
<hr>
</template>
</div>
</div>
</template>
</div>
</div>
</template>
<template v-else-if="tab_list == 'markdown'">
<div v-if="Object.keys(modules_res).length" class="accordion" style="width: 95%">
<div class="accordion-item" v-for="result, key in modules_res">
<template v-if="!('error' in result)">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" :data-bs-target="'#panelsStayOpen-'+key" aria-expanded="true" :aria-controls="'panelsStayOpen-'+key">
[[key]]
</button>
</h2>
<div :id="'panelsStayOpen-'+key" class="accordion-collapse collapse show">
<div class="accordion-body row">
<template v-for="obj, key_obj in result.results.Object">
<h4>Object #[[key_obj+1]] - <small>[[obj.name]]</small></h4>
<div style="margin-bottom: 10px;" v-for="attr, key_attr in obj.Attribute">
<h6>Attributes #[[key_attr+1]]</h6>
<div>
Type: [[attr.type]]
</div>
<div>
Value: [[attr.value]]
</div>
</div>
<hr>
</template>
</div>
</div>
</template>
</div>
</div>
</template>
</template>
</div>
</div>
<span id="goTop">[<a href="#top">Go Back Top</a>]</span>
{% endblock %}
@ -208,16 +262,23 @@
const modules_res = ref({})
const progress = ref(0)
const status_site = ref()
const tab_list = ref("json")
const tab_list = ref("visual")
const history = ref({})
const query_info = ref({})
function actionQuery(){
is_searching.value = true
sid.value = $("#share").val()
pollScan();
}
async function queryInfo(){
sid.value = $("#share").val()
let res = await fetch("/get_query_info/" + sid.value)
let loc = await res.json()
query_info.value = loc
}
function pollScan() {
// Loop function to update the list of identified domains
$.getJSON('/status/' + sid.value, function(data) {
@ -254,13 +315,13 @@
tab_list.value = "json"
if ( !document.getElementById("tab-json").classList.contains("active") ){
document.getElementById("tab-json").classList.add("active")
document.getElementById("tab-parser").classList.remove("active")
document.getElementById("tab-visual").classList.remove("active")
document.getElementById("tab-markdown").classList.remove("active")
}
}else if(active_tab == "parser"){
tab_list.value = "parser"
if ( !document.getElementById("tab-parser").classList.contains("active") ){
document.getElementById("tab-parser").classList.add("active")
}else if(active_tab == "visual"){
tab_list.value = "visual"
if ( !document.getElementById("tab-visual").classList.contains("active") ){
document.getElementById("tab-visual").classList.add("active")
document.getElementById("tab-json").classList.remove("active")
document.getElementById("tab-markdown").classList.remove("active")
}
@ -269,7 +330,7 @@
if ( !document.getElementById("tab-markdown").classList.contains("active") ){
document.getElementById("tab-markdown").classList.add("active")
document.getElementById("tab-json").classList.remove("active")
document.getElementById("tab-parser").classList.remove("active")
document.getElementById("tab-visual").classList.remove("active")
}
}
}
@ -280,13 +341,37 @@
history.value = loc
}
async function query_as_same(value){
let result_dict = {"modules": query_info.value["modules"],
"input": query_info.value["input_query"],
"query": value,
"parent_id": sid.value,
"query_as_same": true,
"config": {}
}
const res = await fetch('/run_modules',{
headers: { "X-CSRFToken": $("#csrf_token").val(), "Content-Type": "application/json" },
method: "POST",
body: JSON.stringify(result_dict)
})
if(await res.status == 201){
let loc = await res.json()
await nextTick()
window.location.href="/query/" + loc['id']
}
}
onMounted(() => {
queryInfo()
actionQuery()
get_history_session()
window._query_as_same = query_as_same
})
return {
message_list,
sid,
query_info,
progress,
status_site,
is_searching,
@ -294,7 +379,10 @@
tab_list,
history,
generateCoreFormatUI,
active_tab
parseMispObject,
parseMispAttr,
active_tab,
query_as_same
}
}
}).mount('.container-fluid')

View File

@ -11,18 +11,34 @@
<a href="/" class="list-group-item list-group-item-action text-nowrap" aria-current="true">
<i class="fa-solid fa-house fa-fw me-3"></i><span>Home</span>
</a>
<a style="margin-top: 30px;" href="/history" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-clock-rotate-left fa-fw me-3"></i><span>History</span>
</a>
{% if session.admin_user %}
{% if current_user.is_authenticated %}
<a style="margin-top: 30px;" href="/history" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-clock-rotate-left fa-fw me-3"></i><span>History</span>
</a>
{%endif%}
{%else%}
<a style="margin-top: 30px;" href="/history" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-clock-rotate-left fa-fw me-3"></i><span>History</span>
</a>
{%endif%}
<a style="margin-top: 30px;" href="/history_session" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-clock fa-fw me-3"></i><span>History Session</span>
</a>
<a style="margin-top: 30px;" href="/history_tree" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-timeline fa-fw me-3"></i><span>History Tree</span>
</a>
<a style="margin-top: 30px;" href="/modules_config" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-gear fa-fw me-3"></i><span>Config</span>
</a>
{% if session.admin_user %}
{% if current_user.is_authenticated %}
<a style="margin-top: 30px;" href="/modules_config" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-gear fa-fw me-3"></i><span>Config</span>
</a>
{%endif%}
{%else%}
<a style="margin-top: 30px;" href="/modules_config" class="list-group-item list-group-item-action text-nowrap">
<i class="fa-solid fa-gear fa-fw me-3"></i><span>Config</span>
</a>
{%endif%}
</div>
</div>
</div>

View File

@ -7,37 +7,39 @@ from .utils import query_get_module
def create_modules_db():
modules = query_get_module()
for module in modules:
m = Module.query.filter_by(name=module["name"]).first()
input_attr = ""
if "input" in module["mispattributes"]:
input_attr = json.dumps(module["mispattributes"]["input"])
if not m:
m = Module(
name=module["name"],
description=module["meta"]["description"],
is_active=True,
request_on_query=False,
input_attr=input_attr
)
db.session.add(m)
db.session.commit()
if not "message" in modules:
for module in modules:
m = Module.query.filter_by(name=module["name"]).first()
input_attr = ""
if "input" in module["mispattributes"]:
input_attr = json.dumps(module["mispattributes"]["input"])
if not m:
m = Module(
name=module["name"],
description=module["meta"]["description"],
is_active=True,
request_on_query=False,
input_attr=input_attr
)
db.session.add(m)
db.session.commit()
if "config" in module["meta"]:
for conf in module["meta"]["config"]:
c = Config.query.filter_by(name=conf).first()
if not c:
c = Config(
name = conf
if "config" in module["meta"]:
for conf in module["meta"]["config"]:
c = Config.query.filter_by(name=conf).first()
if not c:
c = Config(
name = conf
)
db.session.add(c)
db.session.commit()
mc = Module_Config(
module_id=m.id,
config_id=c.id
)
db.session.add(c)
db.session.add(mc)
db.session.commit()
mc = Module_Config(
module_id=m.id,
config_id=c.id
)
db.session.add(mc)
db.session.commit()
else:
print("[-] Error in misp-modules. It might not running.")

View File

@ -1,9 +1,10 @@
# import os
import os
import uuid
import json
import requests
# import jsonschema
from config import Config
from pathlib import Path
MODULES = []
@ -37,83 +38,22 @@ def isUUID(uid):
return True
except ValueError:
return False
def get_object(obj_name):
path = Path(os.getcwd())
parent_path = path.parent.absolute()
loc_path = os.path.join(parent_path, "misp_modules", "lib", "misp-objects", "objects")
if os.path.isdir(loc_path):
with open(os.path.join(loc_path, obj_name, "definition.json"), "r") as read_json:
loc_json = json.load(read_json)
return loc_json
return False
# def form_to_dict(form):
# loc_dict = dict()
# for field in form._fields:
# if field == "files_upload":
# loc_dict[field] = dict()
# loc_dict[field]["data"] = form._fields[field].data
# loc_dict[field]["name"] = form._fields[field].name
# elif not field == "submit" and not field == "csrf_token":
# loc_dict[field] = form._fields[field].data
# return loc_dict
def admin_user_active():
return Config.ADMIN_USER
# def create_specific_dir(specific_dir):
# if not os.path.isdir(specific_dir):
# os.mkdir(specific_dir)
def admin_password():
return Config.ADMIN_PASSWORD
# caseSchema = {
# "type": "object",
# "properties": {
# "title": {"type": "string"},
# "description": {"type": "string"},
# "uuid": {"type": "string"},
# "deadline:": {"type": "string"},
# "recurring_date:": {"type": "string"},
# "recurring_type:": {"type": "string"},
# "tasks": {
# "type": "array",
# "items": {"type": "object"},
# },
# "tags":{
# "type": "array",
# "items": {"type": "string"},
# },
# "clusters":{
# "type": "array",
# "items": {"type": "string"},
# },
# },
# "required": ['title']
# }
# taskSchema = {
# "type": "object",
# "properties": {
# "title": {"type": "string"},
# "description": {"type": "string"},
# "uuid": {"type": "string"},
# "deadline:": {"type": "string"},
# "url:": {"type": "string"},
# "notes:": {"type": "string"},
# "tags":{
# "type": "array",
# "items": {"type": "string"}
# },
# "clusters":{
# "type": "array",
# "items": {"type": "string"},
# },
# },
# "required": ['title']
# }
# def validateCaseJson(json_data):
# try:
# jsonschema.validate(instance=json_data, schema=caseSchema)
# except jsonschema.exceptions.ValidationError as err:
# print(err)
# return False
# return True
# def validateTaskJson(json_data):
# try:
# jsonschema.validate(instance=json_data, schema=taskSchema)
# except jsonschema.exceptions.ValidationError as err:
# print(err)
# return False
# return True

View File

@ -4,6 +4,8 @@ class Config:
FLASK_URL = '127.0.0.1'
FLASK_PORT = 7008
MISP_MODULE = '127.0.0.1:6666'
ADMIN_USER = False
ADMIN_PASSWORD = "Password1234"
class DevelopmentConfig(Config):
DEBUG = True
@ -21,15 +23,9 @@ class TestingConfig(Config):
SQLALCHEMY_DATABASE_URI = "sqlite:///misp-module-test.sqlite"
WTF_CSRF_ENABLED = False
@classmethod
def init_app(cls, app):
print('THIS APP IS IN TESTING MODE. \
YOU SHOULD NOT SEE THIS IN PRODUCTION.')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
# 'production': ProductionConfig,
'default': DevelopmentConfig
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

View File

@ -1,12 +0,0 @@
#!/bin/bash
sudo apt-get update -y
sudo apt-get install virtualenv -y
virtualenv env
source env/bin/activate
pip install -r requirements.txt
python app.py -i
deactivate

View File

@ -1,8 +1,18 @@
#!/bin/bash
isscripted=`screen -ls | egrep '[0-9]+.misp_mod' | cut -d. -f1`
function killscript {
if [ $isscripted ]; then
screen -X -S misp_mod quit
fi
}
function launch {
export FLASKENV="development"
killscript
screen -dmS "misp_mod"
screen -S "misp_mod" -X screen -t "misp_modules_server" bash -c "../env/bin/misp-modules -l 127.0.0.1; read x"
sleep 2
python3 app.py -m
python3 app.py
}
@ -30,6 +40,8 @@ if [ "$1" ]; then
-r | --reload_db ) reload_db;
;;
-t | --test ) test;
;;
-ks | --killscript ) killscript;
esac
shift
else

View File

@ -4,9 +4,9 @@ flask-session
flask-sqlalchemy
Flask-WTF
Flask-Migrate
Flask-Login
WTForms
Werkzeug==2.3.8
Flask-Migrate
flask-restx
python-dateutil
schedule