misp-modules/website/app/templates/query.html

434 lines
22 KiB
HTML
Raw Normal View History

2024-02-08 11:30:55 +01:00
<!--
Author: David Cruciani
-->
{% extends 'base.html' %}
{% block content %}
2024-02-20 14:12:26 +01:00
<br> <br>
<input type="hidden" id="share" value="{{sid}}">
<div id="top" style="display: ruby; margin-top: 40px;">
<h2>{{query}}</h2>
2024-02-08 11:30:55 +01:00
</div>
2024-02-22 14:35:47 +01:00
<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 class="dropdown" style="float: right;">
<button class="btn btn-primary dropdown-toggle" title="New query with same parameters" style="border-radius: 0;" data-bs-toggle="dropdown" aria-expanded="false" data-bs-auto-close="outside">
Query as same
</button>
<div class="dropdown-menu p-4" style="min-width: 200px;">
<div class="mb-3">
<label for="query_as_params" class="form-label">To query:</label>
<input type="email" class="form-control" id="query_as_params" placeholder="{{query}}">
<div id="query_as_params_error" style="color:brown"></div>
</div>
<button type="submit" class="btn btn-primary btn-sm" @click="query_as_params()" style="border-radius: 50px;">Query</button>
</div>
</div>
</div>
2024-02-22 14:35:47 +01:00
2024-02-08 11:30:55 +01:00
2024-02-20 14:12:26 +01:00
<div class="card card-body">
<div class="row">
<div class="col">
<h4>Input Attribute:</h4>
{{input_query}}
2024-02-08 11:30:55 +01:00
</div>
2024-02-20 14:12:26 +01:00
<div class="col">
<h4>Modules:</h4>
{{", ".join(modules)}}
2024-02-09 11:13:14 +01:00
</div>
2024-02-08 11:30:55 +01:00
</div>
2024-02-20 14:12:26 +01:00
<div class="d-flex w-100 justify-content-between">
<div></div>
<small><i>{{query_date}}</i></small>
</div>
2024-02-08 11:30:55 +01:00
</div>
2024-02-20 14:12:26 +01:00
<div style="margin-top: 10px;" v-if="is_searching" class="progress" >
<div class="progress-bar" id="progress" role="progressbar" aria-valuemin="0" aria-valuemax="100" :style="'width:'+progress + '%;'">
[[progress]]%
</div>
</div>
<span v-if="status_site" style="margin-left: 5px; font-size: 13px; float: right;">[[status_site]]</span>
2024-02-08 11:30:55 +01:00
<br/>
<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>
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasScrolling" aria-labelledby="offcanvasScrollingLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasScrollingLabel">Current History query</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div style="margin-left: 18px;">
<a class="btn btn-secondary btn-sm" href="/history_session">Complete view</a>
</div>
<div class="offcanvas-body">
<ul>
<li><a :href="'/query/'+history.uuid" :title="'Attribute: \n' +history.input+ '\n\nModules: \n' + history.modules">[[history.query]]</a></li>
<ul>
<template v-for="child in history.children">
<history_view :history="child"></history_view>
</template>
</ul>
</ul>
</div>
</div>
2024-02-08 11:30:55 +01:00
<!-- Results Part -->
2024-02-12 15:36:38 +01:00
<hr>
<ul class="nav nav-tabs" style="margin-bottom: 10px;">
<li class="nav-item">
<button class="nav-link active" id="tab-visual" @click="active_tab('visual')">Visual</button>
2024-02-12 15:36:38 +01:00
</li>
<li class="nav-item">
<button class="nav-link" id="tab-json" aria-current="page" @click="active_tab('json')">Json</button>
2024-02-19 11:21:17 +01:00
</li>
<li class="nav-item">
<button class="nav-link" id="tab-markdown" @click="active_tab('markdown')">Markdown</button>
2024-02-12 15:36:38 +01:00
</li>
</ul>
<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>
2024-02-12 15:36:38 +01:00
</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>
2024-02-08 11:30:55 +01:00
</div>
</template>
2024-02-12 15:36:38 +01:00
<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)">
2024-02-12 15:36:38 +01:00
<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-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>
2024-02-12 15:36:38 +01:00
</div>
</template>
</div>
</div>
</template>
2024-02-12 15:36:38 +01:00
</div>
2024-02-08 11:30:55 +01:00
<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>
</template>
</div>
</div>
2024-02-19 11:21:17 +01:00
2024-02-08 11:30:55 +01:00
<span id="goTop">[<a href="#top">Go Back Top</a>]</span>
{% endblock %}
{% block script %}
<script type="module">
const { createApp, ref, onMounted, nextTick, defineComponent} = Vue
import {message_list} from '/static/js/toaster.js'
import history_view from '/static/js/history/history_tree_query.js'
2024-02-08 11:30:55 +01:00
createApp({
delimiters: ['[[', ']]'],
components: {
history_view
},
2024-02-08 11:30:55 +01:00
setup() {
const is_searching = ref(false)
const sid = ref(null)
let last_registered = 0
const modules_res = ref({})
const progress = ref(0)
const status_site = ref()
const tab_list = ref("visual")
const history = ref({})
2024-02-22 11:31:14 +01:00
const query_info = ref({})
2024-02-08 11:30:55 +01:00
function actionQuery(){
is_searching.value = true
pollScan();
}
2024-02-22 11:31:14 +01:00
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
}
2024-02-08 11:30:55 +01:00
function pollScan() {
// Loop function to update the list of identified domains
$.getJSON('/status/' + sid.value, function(data) {
progress.value = Math.round((data['complete']/data['total'])*100)
status_site.value = 'Processed ' + data['complete'] + ' of ' + data['total']
if (data['remaining'] > 0) {
setTimeout(pollScan, 3000);
} else {
let sum = data['complete'] - data["nb_errors"]
2024-02-08 11:30:55 +01:00
// Button Stop pressed
if (data['stopped']){
status_site.value = 'Stopped ! ' + sum + ' Success. ' + data["nb_errors"] + ' Errors. ' + data['complete'] + ' Total.'
// Display result of the search
}else{
status_site.value = sum + ' Success. ' + data["nb_errors"] + ' Errors. ' + data['complete'] + ' Total.'
}
2024-02-08 11:30:55 +01:00
}
if (last_registered < data['registered']) {
last_registered = data['registered']
fetchResult();
}
});
}
async function fetchResult(){
modules_res.value = {}
const res = await fetch("/result/"+sid.value)
let loc = await res.json()
modules_res.value = loc
}
2024-02-19 11:21:17 +01:00
function active_tab(active_tab){
if(active_tab == "json"){
tab_list.value = "json"
2024-02-12 15:36:38 +01:00
if ( !document.getElementById("tab-json").classList.contains("active") ){
document.getElementById("tab-json").classList.add("active")
document.getElementById("tab-visual").classList.remove("active")
2024-02-19 11:21:17 +01:00
document.getElementById("tab-markdown").classList.remove("active")
2024-02-12 15:36:38 +01:00
}
}else if(active_tab == "visual"){
tab_list.value = "visual"
if ( !document.getElementById("tab-visual").classList.contains("active") ){
document.getElementById("tab-visual").classList.add("active")
2024-02-12 15:36:38 +01:00
document.getElementById("tab-json").classList.remove("active")
2024-02-19 11:21:17 +01:00
document.getElementById("tab-markdown").classList.remove("active")
}
}else if(active_tab == "markdown"){
tab_list.value = "markdown"
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-visual").classList.remove("active")
2024-02-12 15:36:38 +01:00
}
}
}
async function get_history_session(){
let res = await fetch("/get_current_query_history")
let loc = await res.json()
history.value = loc
}
// query 'value' with same parameters with a parent
2024-02-22 11:31:14 +01:00
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']
}
}
// query 'value' with same parameters without a parent
async function query_as_params(){
let loc = $("#query_as_params").val()
$("#query_as_params_error").text("")
if(loc){
let result_dict = {"modules": query_info.value["modules"],
"input": query_info.value["input_query"],
"query": loc,
"config": {},
"same_query_id": sid.value,
"parent_id": ""
}
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']
}
}else{
$("#query_as_params_error").text("Please give value")
}
}
2024-02-08 11:30:55 +01:00
onMounted(() => {
2024-02-22 11:31:14 +01:00
queryInfo()
2024-02-08 11:30:55 +01:00
actionQuery()
get_history_session()
2024-02-22 11:31:14 +01:00
window._query_as_same = query_as_same
2024-02-08 11:30:55 +01:00
})
return {
message_list,
sid,
2024-02-22 11:31:14 +01:00
query_info,
2024-02-08 11:30:55 +01:00
progress,
status_site,
is_searching,
modules_res,
2024-02-19 11:21:17 +01:00
tab_list,
history,
2024-02-12 15:36:38 +01:00
generateCoreFormatUI,
parseMispObject,
parseMispAttr,
active_tab,
query_as_same,
query_as_params
2024-02-08 11:30:55 +01:00
}
}
2024-02-19 14:01:06 +01:00
}).mount('.container-fluid')
2024-02-08 11:30:55 +01:00
</script>
{% endblock %}