Compare commits
No commits in common. "837227b9e01bc215d51dc0cec93336020846263b" and "610c1b463376e39d1aa1226139356a39272ed5b0" have entirely different histories.
837227b9e0
...
610c1b4633
11 changed files with 16 additions and 124 deletions
|
@ -4,6 +4,7 @@ misp_apikey = 'FI4gCRghRZvLVjlLPLTFZ852x2njkkgPSz0zQ3E0'
|
||||||
misp_skipssl = True
|
misp_skipssl = True
|
||||||
|
|
||||||
live_logs_accepted_scope = {
|
live_logs_accepted_scope = {
|
||||||
|
'rest_client_history': '*',
|
||||||
'events': ['add', 'edit', 'delete', 'restSearch',],
|
'events': ['add', 'edit', 'delete', 'restSearch',],
|
||||||
'attributes': ['add', 'edit', 'delete', 'restSearch',],
|
'attributes': ['add', 'edit', 'delete', 'restSearch',],
|
||||||
'tags': '*',
|
'tags': '*',
|
||||||
|
|
25
exercise.py
25
exercise.py
|
@ -15,9 +15,6 @@ ACTIVE_EXERCISES_DIR = "active_exercises"
|
||||||
|
|
||||||
def load_exercises() -> bool:
|
def load_exercises() -> bool:
|
||||||
db.ALL_EXERCISES = read_exercise_dir()
|
db.ALL_EXERCISES = read_exercise_dir()
|
||||||
if not is_validate_exercises(db.ALL_EXERCISES):
|
|
||||||
print('Issue while validating exercises')
|
|
||||||
return False
|
|
||||||
init_inject_flow()
|
init_inject_flow()
|
||||||
init_exercises_tasks()
|
init_exercises_tasks()
|
||||||
return True
|
return True
|
||||||
|
@ -35,28 +32,6 @@ def read_exercise_dir():
|
||||||
return exercises
|
return exercises
|
||||||
|
|
||||||
|
|
||||||
def is_validate_exercises(exercises: list) -> bool:
|
|
||||||
exercises_uuid = set()
|
|
||||||
tasks_uuid = set()
|
|
||||||
exercise_by_uuid = {}
|
|
||||||
task_by_uuid = {}
|
|
||||||
for exercise in exercises:
|
|
||||||
e_uuid = exercise['exercise']['uuid']
|
|
||||||
if e_uuid in exercises_uuid:
|
|
||||||
print(f'Duplicated UUID {e_uuid}. ({exercise['exercise']['name']}, {exercise_by_uuid[e_uuid]['exercise']['name']})')
|
|
||||||
return False
|
|
||||||
exercises_uuid.add(e_uuid)
|
|
||||||
exercise_by_uuid[e_uuid] = exercise
|
|
||||||
for inject in exercise['injects']:
|
|
||||||
t_uuid = inject['uuid']
|
|
||||||
if t_uuid in tasks_uuid:
|
|
||||||
print(f'Duplicated UUID {t_uuid}. ({inject['name']}, {task_by_uuid[t_uuid]['name']})')
|
|
||||||
return False
|
|
||||||
tasks_uuid.add(t_uuid)
|
|
||||||
task_by_uuid[t_uuid] = inject
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def init_inject_flow():
|
def init_inject_flow():
|
||||||
for exercise in db.ALL_EXERCISES:
|
for exercise in db.ALL_EXERCISES:
|
||||||
for inject in exercise['injects']:
|
for inject in exercise['injects']:
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"state:production"
|
"state:production"
|
||||||
],
|
],
|
||||||
"total_duration": "7200",
|
"total_duration": "7200",
|
||||||
"uuid": "eb00428f-40b5-4da7-9402-7ce22a840659",
|
"uuid": "29324587-db6c-4a73-a209-cf8c79871629",
|
||||||
"version": "20240624"
|
"version": "20240624"
|
||||||
},
|
},
|
||||||
"inject_flow": [
|
"inject_flow": [
|
||||||
|
|
20
misp_api.py
20
misp_api.py
|
@ -44,23 +44,3 @@ def doRestQuery(authkey: str, request_method: str, url: str, payload: dict = {})
|
||||||
return post(url, payload, api_key=authkey)
|
return post(url, payload, api_key=authkey)
|
||||||
else:
|
else:
|
||||||
return get(url, payload, api_key=authkey)
|
return get(url, payload, api_key=authkey)
|
||||||
|
|
||||||
|
|
||||||
def getSettings() -> Union[None, dict]:
|
|
||||||
SETTING_TO_QUERY = [
|
|
||||||
'Plugin.ZeroMQ_enable',
|
|
||||||
'Plugin.ZeroMQ_audit_notifications_enable',
|
|
||||||
'Plugin.ZeroMQ_event_notifications_enable',
|
|
||||||
'Plugin.ZeroMQ_attribute_notifications_enable',
|
|
||||||
'MISP.log_paranoid',
|
|
||||||
'MISP.log_paranoid_skip_db',
|
|
||||||
'MISP.log_paranoid_include_post_body',
|
|
||||||
'MISP.log_auth',
|
|
||||||
'Security.allow_unsafe_cleartext_apikey_logging',
|
|
||||||
]
|
|
||||||
settings = get(f'/servers/serverSettings.json')
|
|
||||||
if not settings:
|
|
||||||
return None
|
|
||||||
return {
|
|
||||||
setting['setting']: setting['value'] for setting in settings.get('finalSettings', []) if setting['setting'] in SETTING_TO_QUERY
|
|
||||||
}
|
|
|
@ -41,9 +41,8 @@ def get_user_authkey_id_pair(data: dict):
|
||||||
if 'user_id' in data and 'title' in data :
|
if 'user_id' in data and 'title' in data :
|
||||||
if data['title'].startswith('Successful authentication using API key'):
|
if data['title'].startswith('Successful authentication using API key'):
|
||||||
authkey_search = re.search(authkey_title_regex, data['title'], re.IGNORECASE)
|
authkey_search = re.search(authkey_title_regex, data['title'], re.IGNORECASE)
|
||||||
if authkey_search is not None:
|
authkey = authkey_search.group(1)
|
||||||
authkey = authkey_search.group(1)
|
return (int(data['user_id']), authkey,)
|
||||||
return (int(data['user_id']), authkey,)
|
|
||||||
return (None, None,)
|
return (None, None,)
|
||||||
|
|
||||||
|
|
||||||
|
|
16
server.py
16
server.py
|
@ -11,7 +11,6 @@ from eventlet.green import zmq as gzmq
|
||||||
import exercise as exercise_model
|
import exercise as exercise_model
|
||||||
import notification as notification_model
|
import notification as notification_model
|
||||||
import db
|
import db
|
||||||
import misp_api
|
|
||||||
|
|
||||||
|
|
||||||
# Initialize ZeroMQ context and subscriber socket
|
# Initialize ZeroMQ context and subscriber socket
|
||||||
|
@ -69,10 +68,6 @@ def mark_task_incomplete(sid, payload):
|
||||||
def reset_all_exercise_progress(sid):
|
def reset_all_exercise_progress(sid):
|
||||||
return exercise_model.resetAllExerciseProgress()
|
return exercise_model.resetAllExerciseProgress()
|
||||||
|
|
||||||
@sio.event
|
|
||||||
def get_diagnostic(sid):
|
|
||||||
return getDiagnostic()
|
|
||||||
|
|
||||||
@sio.on('*')
|
@sio.on('*')
|
||||||
def any_event(event, sid, data={}):
|
def any_event(event, sid, data={}):
|
||||||
print('>> Unhandled event', event)
|
print('>> Unhandled event', event)
|
||||||
|
@ -119,20 +114,15 @@ def get_context(data: dict) -> dict:
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
def getDiagnostic() -> dict:
|
|
||||||
misp_settings = misp_api.getSettings()
|
|
||||||
return {
|
|
||||||
'settings': misp_settings,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Function to forward zmq messages to Socket.IO
|
# Function to forward zmq messages to Socket.IO
|
||||||
def forward_zmq_to_socketio():
|
def forward_zmq_to_socketio():
|
||||||
while True:
|
while True:
|
||||||
message = zsocket.recv_string()
|
message = zsocket.recv_string()
|
||||||
topic, s, m = message.partition(" ")
|
topic, s, m = message.partition(" ")
|
||||||
|
handleMessage(topic, s, m)
|
||||||
try:
|
try:
|
||||||
handleMessage(topic, s, m)
|
pass
|
||||||
|
# handleMessage(topic, s, m)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { exercises, selected_exercises, diagnostic, resetAllExerciseProgress, changeExerciseSelection, fetchDiagnostic } from "@/socket";
|
import { exercises, selected_exercises, resetAllExerciseProgress, changeExerciseSelection } from "@/socket";
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||||
import { faScrewdriverWrench, faTrash, faSuitcaseMedical, faGraduationCap } from '@fortawesome/free-solid-svg-icons'
|
import { faScrewdriverWrench, faTrash } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
const admin_modal = ref(null)
|
const admin_modal = ref(null)
|
||||||
|
|
||||||
|
@ -10,15 +10,11 @@
|
||||||
changeExerciseSelection(exec_uuid, state_enabled);
|
changeExerciseSelection(exec_uuid, state_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showTheModal() {
|
|
||||||
admin_modal.value.showModal()
|
|
||||||
fetchDiagnostic()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
@click="showTheModal()"
|
@click="admin_modal.showModal()"
|
||||||
class="px-2 py-1 rounded-md focus-outline font-semibold bg-blue-600 text-slate-200 hover:bg-blue-700"
|
class="px-2 py-1 rounded-md focus-outline font-semibold bg-blue-600 text-slate-200 hover:bg-blue-700"
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon :icon="faScrewdriverWrench" class="mr-1"></FontAwesomeIcon>
|
<FontAwesomeIcon :icon="faScrewdriverWrench" class="mr-1"></FontAwesomeIcon>
|
||||||
|
@ -48,10 +44,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="text-lg font-semibold">
|
<h3 class="text-lg font-semibold">Selected Exercises</h3>
|
||||||
<FontAwesomeIcon :icon="faGraduationCap" class="mr-1"></FontAwesomeIcon>
|
|
||||||
Selected Exercises
|
|
||||||
</h3>
|
|
||||||
<div
|
<div
|
||||||
v-for="(exercise) in exercises"
|
v-for="(exercise) in exercises"
|
||||||
:key="exercise.name"
|
:key="exercise.name"
|
||||||
|
@ -69,33 +62,6 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 class="text-lg font-semibold mt-4">
|
|
||||||
<FontAwesomeIcon :icon="faSuitcaseMedical" class="mr-1"></FontAwesomeIcon>
|
|
||||||
Diagnostic
|
|
||||||
</h3>
|
|
||||||
<div class="ml-3">
|
|
||||||
<div v-if="Object.keys(diagnostic).length == 0" class="flex justify-center">
|
|
||||||
<span class="loading loading-dots loading-lg"></span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-for="(value, setting) in diagnostic['settings']"
|
|
||||||
:key="setting"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<label class="label cursor-pointer justify-start p-0 pt-1">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="value"
|
|
||||||
:value="setting"
|
|
||||||
:class="`checkbox ${value ? 'checkbox-success' : 'checkbox-danger'}`"
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
<span class="font-mono font-semibold text-base ml-3">{{ setting }}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form method="dialog" class="modal-backdrop">
|
<form method="dialog" class="modal-backdrop">
|
||||||
|
|
|
@ -1,24 +1,14 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { active_exercises as exercises, progresses, setCompletedState } from "@/socket";
|
import { active_exercises as exercises, progresses, setCompletedState } from "@/socket";
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||||
import { faCheck, faTimes, faGraduationCap } from '@fortawesome/free-solid-svg-icons'
|
import { faCheck, faTimes, faGraduationCap } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
|
||||||
const collapsed_panels = ref([])
|
|
||||||
|
|
||||||
function toggleCompleted(completed, user_id, exec_uuid, task_uuid) {
|
function toggleCompleted(completed, user_id, exec_uuid, task_uuid) {
|
||||||
setCompletedState(completed, user_id, exec_uuid, task_uuid)
|
setCompletedState(completed, user_id, exec_uuid, task_uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
function collapse(exercise_index) {
|
|
||||||
const index = collapsed_panels.value.indexOf(exercise_index)
|
|
||||||
if (index >= 0) {
|
|
||||||
collapsed_panels.value.splice(index, 1)
|
|
||||||
} else {
|
|
||||||
collapsed_panels.value.push(exercise_index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasExercises = computed(() => exercises.value.length > 0)
|
const hasExercises = computed(() => exercises.value.length > 0)
|
||||||
const hasProgress = computed(() => Object.keys(progresses.value).length > 0)
|
const hasProgress = computed(() => Object.keys(progresses.value).length > 0)
|
||||||
|
|
||||||
|
@ -42,7 +32,7 @@
|
||||||
class="bg-white dark:bg-slate-800 rounded-lg shadow-xl w-full mb-4"
|
class="bg-white dark:bg-slate-800 rounded-lg shadow-xl w-full mb-4"
|
||||||
>
|
>
|
||||||
<thead>
|
<thead>
|
||||||
<tr @click="collapse(exercise_index)" class="cursor-pointer">
|
<tr>
|
||||||
<th :colspan="2 + exercise.tasks.length" class="rounded-t-lg border-b border-slate-100 dark:border-slate-700 text-md p-3 pl-6 text-center dark:bg-blue-800 bg-blue-500 dark:text-slate-300 text-slate-100">
|
<th :colspan="2 + exercise.tasks.length" class="rounded-t-lg border-b border-slate-100 dark:border-slate-700 text-md p-3 pl-6 text-center dark:bg-blue-800 bg-blue-500 dark:text-slate-300 text-slate-100">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<span class="dark:text-blue-200 text-slate-200 "># {{ exercise_index + 1 }}</span>
|
<span class="dark:text-blue-200 text-slate-200 "># {{ exercise_index + 1 }}</span>
|
||||||
|
@ -58,7 +48,7 @@
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr :class="`font-medium text-slate-600 dark:text-slate-200 ${collapsed_panels.includes(exercise_index) ? 'hidden' : ''}`">
|
<tr class="font-medium text-slate-600 dark:text-slate-200">
|
||||||
<th class="border-b border-slate-100 dark:border-slate-700 p-3 pl-6 text-left">User</th>
|
<th class="border-b border-slate-100 dark:border-slate-700 p-3 pl-6 text-left">User</th>
|
||||||
<th
|
<th
|
||||||
v-for="(task, task_index) in exercise.tasks"
|
v-for="(task, task_index) in exercise.tasks"
|
||||||
|
@ -73,7 +63,7 @@
|
||||||
<th class="border-b border-slate-100 dark:border-slate-700 p-3 text-left">Progress</th>
|
<th class="border-b border-slate-100 dark:border-slate-700 p-3 text-left">Progress</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody :class="`${collapsed_panels.includes(exercise_index) ? 'hidden' : ''}`">
|
<tbody>
|
||||||
<tr v-if="!hasProgress">
|
<tr v-if="!hasProgress">
|
||||||
<td
|
<td
|
||||||
:colspan="2 + exercise.tasks.length"
|
:colspan="2 + exercise.tasks.length"
|
||||||
|
|
|
@ -12,7 +12,6 @@ const initial_state = {
|
||||||
exercises: [],
|
exercises: [],
|
||||||
selected_exercises: [],
|
selected_exercises: [],
|
||||||
progresses: {},
|
progresses: {},
|
||||||
diagnostic: {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = reactive({ ...initial_state });
|
const state = reactive({ ...initial_state });
|
||||||
|
@ -28,7 +27,6 @@ export const notifications = computed(() => state.notificationEvents)
|
||||||
export const notificationCounter = computed(() => state.notificationCounter)
|
export const notificationCounter = computed(() => state.notificationCounter)
|
||||||
export const notificationAPICounter = computed(() => state.notificationAPICounter)
|
export const notificationAPICounter = computed(() => state.notificationAPICounter)
|
||||||
export const userCount = computed(() => Object.keys(state.progresses).length)
|
export const userCount = computed(() => Object.keys(state.progresses).length)
|
||||||
export const diagnostic = computed(() => state.diagnostic)
|
|
||||||
export const socketConnected = computed(() => connectionState.connected)
|
export const socketConnected = computed(() => connectionState.connected)
|
||||||
|
|
||||||
export function resetState() {
|
export function resetState() {
|
||||||
|
@ -50,13 +48,6 @@ export function fullReload() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function fetchDiagnostic() {
|
|
||||||
state.diagnostic = {}
|
|
||||||
socket.emit("get_diagnostic", (diagnostic) => {
|
|
||||||
state.diagnostic = diagnostic
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setCompletedState(completed, user_id, exec_uuid, task_uuid) {
|
export function setCompletedState(completed, user_id, exec_uuid, task_uuid) {
|
||||||
const payload = {
|
const payload = {
|
||||||
user_id: user_id,
|
user_id: user_id,
|
||||||
|
|
Loading…
Reference in a new issue