new: [app] Better setting split and added misp setting remediation
This commit is contained in:
parent
81bb48ff54
commit
2cd4820f1c
11 changed files with 157 additions and 70 deletions
39
appConfig.py
Normal file
39
appConfig.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
live_logs_accepted_scope = {
|
||||
'events': ['add', 'edit', 'delete', 'restSearch',],
|
||||
'attributes': ['add', 'add_attachment', 'edit', 'revise_object', 'delete', 'restSearch',],
|
||||
'eventReports': ['add', 'edit', 'delete',],
|
||||
'tags': '*',
|
||||
}
|
||||
|
||||
user_activity_accepted_scope = {
|
||||
'events': ['view', 'add', 'edit', 'delete', 'restSearch',],
|
||||
'attributes': ['add', 'add_attachment', 'edit', 'delete', 'restSearch',],
|
||||
'objects': ['add', 'edit', 'revise_object', 'delete',],
|
||||
'eventReports': ['view', 'add', 'edit', 'delete',],
|
||||
'tags': '*',
|
||||
}
|
||||
|
||||
misp_settings = {
|
||||
'Plugin.ZeroMQ_enable': True,
|
||||
'Plugin.ZeroMQ_audit_notifications_enable': True,
|
||||
'Plugin.ZeroMQ_event_notifications_enable': True,
|
||||
'Plugin.ZeroMQ_attribute_notifications_enable': True,
|
||||
'MISP.log_new_audit': False,
|
||||
'MISP.log_paranoid': True,
|
||||
'MISP.log_paranoid_skip_db': True,
|
||||
'MISP.log_paranoid_include_post_body': True,
|
||||
'MISP.log_auth': True,
|
||||
'Security.allow_unsafe_cleartext_apikey_logging': True,
|
||||
}
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('misp-exercise-dashboard')
|
||||
format = '[%(levelname)s] %(asctime)s - %(message)s'
|
||||
formatter = logging.Formatter(format)
|
||||
logging.basicConfig(filename='misp-exercise-dashboard.log', encoding='utf-8', level=logging.DEBUG, format=format)
|
||||
# create console handler and set level to debug
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.INFO)
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
|
@ -6,28 +6,3 @@ zmq_url = 'tcp://localhost:50000'
|
|||
misp_url = 'https://localhost/'
|
||||
misp_apikey = 'FI4gCRghRZvLVjlLPLTFZ852x2njkkgPSz0zQ3E0'
|
||||
misp_skipssl = True
|
||||
|
||||
live_logs_accepted_scope = {
|
||||
'events': ['add', 'edit', 'delete', 'restSearch',],
|
||||
'attributes': ['add', 'add_attachment', 'edit', 'revise_object', 'delete', 'restSearch',],
|
||||
'eventReports': ['add', 'edit', 'delete',],
|
||||
'tags': '*',
|
||||
}
|
||||
|
||||
user_activity_accepted_scope = {
|
||||
'events': ['view', 'add', 'edit', 'delete', 'restSearch',],
|
||||
'attributes': ['add', 'add_attachment', 'edit', 'delete', 'restSearch',],
|
||||
'objects': ['add', 'edit', 'revise_object', 'delete',],
|
||||
'eventReports': ['view', 'add', 'edit', 'delete',],
|
||||
'tags': '*',
|
||||
}
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('misp-exercise-dashboard')
|
||||
format = '[%(levelname)s] %(asctime)s - %(message)s'
|
||||
formatter = logging.Formatter(format)
|
||||
logging.basicConfig(filename='misp-exercise-dashboard.log', encoding='utf-8', level=logging.DEBUG, format=format)
|
||||
ch = logging.StreamHandler()
|
||||
ch.setLevel(logging.INFO)
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
||||
|
|
|
@ -11,8 +11,7 @@ import jq
|
|||
import db
|
||||
from inject_evaluator import eval_data_filtering, eval_query_mirror, eval_query_search
|
||||
import misp_api
|
||||
import config
|
||||
from config import logger
|
||||
from appConfig import logger
|
||||
|
||||
|
||||
ACTIVE_EXERCISES_DIR = "active_exercises"
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Union
|
|||
import jq
|
||||
import re
|
||||
import operator
|
||||
from config import logger
|
||||
from appConfig import logger
|
||||
|
||||
|
||||
def jq_extract(path: str, data: dict, extract_type='first'):
|
||||
|
|
33
misp_api.py
33
misp_api.py
|
@ -11,7 +11,8 @@ from requests_cache import CachedSession
|
|||
from requests.packages.urllib3.exceptions import InsecureRequestWarning # type: ignore
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
from config import misp_url, misp_apikey, misp_skipssl, logger
|
||||
from config import misp_url, misp_apikey, misp_skipssl
|
||||
from appConfig import logger, misp_settings
|
||||
|
||||
requestSession = CachedSession(cache_name='misp_cache', expire_after=timedelta(seconds=5))
|
||||
adapterCache = requests.adapters.HTTPAdapter(pool_connections=50, pool_maxsize=50)
|
||||
|
@ -83,20 +84,24 @@ async def getVersion() -> Union[None, dict]:
|
|||
|
||||
|
||||
async 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 = await 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
|
||||
data = {}
|
||||
for settingName, expectedSettingValue in misp_settings.items():
|
||||
data[settingName] = {
|
||||
'expected_value': expectedSettingValue,
|
||||
'value': None
|
||||
}
|
||||
for setting in settings.get('finalSettings', []):
|
||||
if setting['setting'] in misp_settings:
|
||||
data[setting['setting']]['value'] = setting['value']
|
||||
return data
|
||||
|
||||
|
||||
async def remediateSetting(setting) ->dict:
|
||||
if setting in misp_settings:
|
||||
payload = {
|
||||
'value': misp_settings[setting],
|
||||
}
|
||||
return await post(f'/servers/serverSettingsEdit/{setting}', payload)
|
|
@ -5,6 +5,7 @@ import re
|
|||
from typing import Union
|
||||
import db
|
||||
import config
|
||||
import appConfig
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
|
||||
|
@ -192,10 +193,10 @@ def is_accepted_notification(notification) -> bool:
|
|||
return False
|
||||
|
||||
scope, action = get_scope_action_from_url(notification['url'])
|
||||
if scope in config.live_logs_accepted_scope:
|
||||
if config.live_logs_accepted_scope == '*':
|
||||
if scope in appConfig.live_logs_accepted_scope:
|
||||
if appConfig.live_logs_accepted_scope == '*':
|
||||
return True
|
||||
elif action in config.live_logs_accepted_scope[scope]:
|
||||
elif action in appConfig.live_logs_accepted_scope[scope]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -209,9 +210,9 @@ def is_accepted_user_activity(notification) -> bool:
|
|||
return False
|
||||
|
||||
scope, action = get_scope_action_from_url(notification['url'])
|
||||
if scope in config.user_activity_accepted_scope:
|
||||
if config.user_activity_accepted_scope == '*':
|
||||
if scope in appConfig.user_activity_accepted_scope:
|
||||
if appConfig.user_activity_accepted_scope == '*':
|
||||
return True
|
||||
elif action in config.user_activity_accepted_scope[scope]:
|
||||
elif action in appConfig.user_activity_accepted_scope[scope]:
|
||||
return True
|
||||
return False
|
11
server.py
11
server.py
|
@ -14,7 +14,7 @@ import exercise as exercise_model
|
|||
import notification as notification_model
|
||||
import db
|
||||
import config
|
||||
from config import logger
|
||||
from appConfig import logger
|
||||
import misp_api
|
||||
|
||||
|
||||
|
@ -123,6 +123,10 @@ async def toggle_verbose_mode(sid, payload):
|
|||
async def toggle_apiquery_mode(sid, payload):
|
||||
return notification_model.set_apiquery_mode(payload['apiquery'])
|
||||
|
||||
@sio.event
|
||||
async def remediate_setting(sid, payload):
|
||||
return await doSettingRemediation(payload['name'])
|
||||
|
||||
@sio.on('*')
|
||||
async def any_event(event, sid, data={}):
|
||||
logger.info('>> Unhandled event %s', event)
|
||||
|
@ -201,6 +205,11 @@ async def getDiagnostic() -> dict:
|
|||
return diagnostic
|
||||
|
||||
|
||||
async def doSettingRemediation(setting) -> dict:
|
||||
result = await misp_api.remediateSetting(setting)
|
||||
return result
|
||||
|
||||
|
||||
async def notification_history():
|
||||
global ZMQ_MESSAGE_COUNT_LAST_TIMESPAN
|
||||
while True:
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { exercises, selected_exercises, diagnostic, fullReload, resetAllExerciseProgress, resetLiveLogs, changeExerciseSelection, debouncedGetDiangostic } from "@/socket";
|
||||
import { exercises, selected_exercises, diagnostic, fullReload, resetAllExerciseProgress, resetLiveLogs, changeExerciseSelection, debouncedGetDiangostic, remediateSetting } from "@/socket";
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
import { faScrewdriverWrench, faTrash, faSuitcaseMedical, faGraduationCap, faBan, faRotate } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faScrewdriverWrench, faTrash, faSuitcaseMedical, faGraduationCap, faBan, faRotate, faHammer, faCheck } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
const admin_modal = ref(null)
|
||||
const clickedButtons = ref([])
|
||||
|
||||
const diagnosticLoading = computed(() => Object.keys(diagnostic.value).length == 0)
|
||||
const isMISPOnline = computed(() => diagnostic.value.version?.version !== undefined)
|
||||
|
@ -15,10 +16,16 @@
|
|||
changeExerciseSelection(exec_uuid, state_enabled);
|
||||
}
|
||||
|
||||
function settingHandler(setting) {
|
||||
remediateSetting(setting)
|
||||
}
|
||||
|
||||
function showTheModal() {
|
||||
admin_modal.value.showModal()
|
||||
clickedButtons.value = []
|
||||
debouncedGetDiangostic()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -131,23 +138,54 @@
|
|||
<div v-if="diagnosticLoading" class="flex justify-center">
|
||||
<span class="loading loading-dots loading-lg"></span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(value, setting) in diagnostic['settings']"
|
||||
<table v-else class="bg-white dark:bg-slate-700 rounded-lg shadow-xl w-full mt-2">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="border-b border-slate-200 dark:border-slate-600 p-2 text-left">Setting</th>
|
||||
<th class="border-b border-slate-200 dark:border-slate-600 p-2 text-left">Value</th>
|
||||
<th class="border-b border-slate-200 dark:border-slate-600 p-2 text-left">Expected Value</th>
|
||||
<th class="border-b border-slate-200 dark:border-slate-600 p-2 text-center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(settingValues, 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'} [--fallback-bc:#cbd5e1]`"
|
||||
disabled
|
||||
/>
|
||||
<span class="font-mono font-semibold text-base ml-3">{{ setting }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<td class="font-mono font-semibold text-base px-2">{{ setting }}</td>
|
||||
<td
|
||||
:class="`font-mono text-base tracking-tight px-2 ${settingValues.expected_value != settingValues.value ? 'text-red-600 dark:text-red-600' : ''}`"
|
||||
>
|
||||
<i v-if="settingValues.value === undefined || settingValues.value === null" class="text-nowrap">- none -</i>
|
||||
{{ settingValues.value }}
|
||||
</td>
|
||||
<td class="font-mono text-base tracking-tight px-2">{{ settingValues.expected_value }}</td>
|
||||
<td class="px-2 text-center">
|
||||
<span v-if="settingValues.error === true"
|
||||
class="text-red-600 dark:text-red-600"
|
||||
>Error: {{ settingValues.errorMessage }}</span>
|
||||
<button
|
||||
v-else-if="settingValues.expected_value != settingValues.value"
|
||||
@click="clickedButtons.push(setting) && settingHandler(setting)"
|
||||
:disabled="clickedButtons.includes(setting)"
|
||||
class="h-8 min-h-8 px-2 font-semibold bg-green-600 text-slate-200 hover:bg-green-700 btn gap-1"
|
||||
>
|
||||
<template v-if="!clickedButtons.includes(setting)">
|
||||
<FontAwesomeIcon :icon="faHammer" size="sm" fixed-width></FontAwesomeIcon>
|
||||
Remediate
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="loading loading-dots loading-sm"></span>
|
||||
</template>
|
||||
</button>
|
||||
<span v-else class="text-base font-bold text-green-600 dark:text-green-600">
|
||||
<FontAwesomeIcon :icon="faCheck" class=""></FontAwesomeIcon>
|
||||
OK
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
|
||||
<table class="bg-white dark:bg-slate-800 rounded-lg shadow-xl w-full">
|
||||
<thead>
|
||||
<tr class="font-medium dark:text-slate-200 text-slate-600 ">
|
||||
<tr class="font-medium dark:text-slate-200 text-slate-600">
|
||||
<th class="border-b border-slate-100 dark:border-slate-700 p-3 pl-6 text-left"></th>
|
||||
<th class="border-b border-slate-100 dark:border-slate-700 p-3 pl-2 text-left">User</th>
|
||||
<th class="border-b border-slate-100 dark:border-slate-700 p-3 text-left">Time</th>
|
||||
|
|
0
src/components/elements/SettingIndicator.vue
Normal file
0
src/components/elements/SettingIndicator.vue
Normal file
|
@ -96,6 +96,18 @@ export function toggleApiQueryMode(enabled) {
|
|||
sendToggleApiQueryMode(enabled)
|
||||
}
|
||||
|
||||
export function remediateSetting(setting) {
|
||||
sendRemediateSetting(setting, (result) => {
|
||||
console.log(result);
|
||||
if (result.success) {
|
||||
state.diagnostic['settings'][setting].value = state.diagnostic['settings'][setting].expected_value
|
||||
} else {
|
||||
state.diagnostic['settings'][setting].error = true
|
||||
state.diagnostic['settings'][setting].errorMessage = result.message
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const debouncedGetProgress = debounce(getProgress, 200, {leading: true})
|
||||
export const debouncedGetDiangostic = debounce(getDiangostic, 1000, {leading: true})
|
||||
|
||||
|
@ -181,6 +193,15 @@ function sendToggleApiQueryMode(enabled) {
|
|||
socket.emit("toggle_apiquery_mode", payload, () => {})
|
||||
}
|
||||
|
||||
function sendRemediateSetting(setting, cb) {
|
||||
const payload = {
|
||||
name: setting
|
||||
}
|
||||
socket.emit("remediate_setting", payload, (result) => {
|
||||
cb(result)
|
||||
})
|
||||
}
|
||||
|
||||
/* Event listener */
|
||||
|
||||
socket.on("connect", () => {
|
||||
|
|
Loading…
Reference in a new issue