First commit

This commit is contained in:
Félix Aime
2020-11-24 19:45:03 +01:00
parent c042b71634
commit 513f6b1b02
130 changed files with 76873 additions and 0 deletions

View File

@ -0,0 +1,75 @@
<template>
<div class="backend-content" id="content">
<div class="column col-6 col-xs-12">
<h3 class="s-title">Manage database</h3>
<ul class="tab tab-block">
<li class="tab-item">
<a href="#" v-on:click="switch_tab('import')" v-bind:class="{ active: tabs.import }">Import database</a>
</li>
<li class="tab-item">
<a href="#" v-on:click="switch_tab('export')" v-bind:class="{ active: tabs.export }">Export database</a>
</li>
</ul>
<div v-if="tabs.export">
<iframe :src="export_url" class="frame-export"></iframe>
</div>
<div v-if="tabs.import">
<label class="form-upload empty" for="upload">
<input type="file" class="upload-field" id="upload" @change="import_from_file">
<p class="empty-title h5">Drop or select a database to import.</p>
<p class="empty-subtitle">The database needs to be an export from a TinyCheck instance.</p>
</label>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'db-manage',
data() {
return {
tabs: { "import" : true, "export" : false },
jwt:""
}
},
props: { },
methods: {
switch_tab: function(tab) {
Object.keys(this.tabs).forEach(key => {
if( key == tab ){
this.tabs[key] = true
} else {
this.tabs[key] = false
}
});
},
import_from_file: function(ev) {
var formData = new FormData();
formData.append("file", ev.target.files[0]);
axios.post('/api/config/db/import', formData, {
headers: {
"Content-Type" : "multipart/form-data",
"X-Token" : this.jwt
}
})
},
async get_jwt(){
await axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if(response.data.token){
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
}
},
created: function() {
this.get_jwt().then(() => {
this.export_url = `/api/config/db/export?token=${this.jwt}`
});
}
}
</script>

View File

@ -0,0 +1,202 @@
<template>
<div class="backend-content" id="content">
<div v-bind:class="{ 'alert-toaster-visible' : toaster.show, 'alert-toaster-hidden' : !toaster.show }">{{toaster.message}}</div>
<div class="modal active" id="modal-id" v-if="check_certificate">
<a href="#close" class="modal-overlay" aria-label="Close"></a>
<div class="modal-container">
<div class="modal-header">
<a href="#close" class="btn btn-clear float-right" aria-label="Close" @click="check_certificate = false"></a>
<div class="modal-title h5">Certificate validation</div>
</div>
<div class="modal-body">
<div class="content">
Do you trust this certificate?
<pre class="code" data-lang="CERTIFICATE">
<code>{{certificate}}</code>
</pre>
</div>
</div>
<div class="modal-footer">
<div class="modal-footer">
<button class="btn btn-primary" @click="validate_server()">Yes I trust it.</button><a class="btn btn-link" href="#modals" @click="no_trust()">No I don't trust it</a>
</div>
</div>
</div>
</div>
<div class="column col-6 col-xs-12">
<h3 class="s-title">Configuration </h3>
<h5 class="s-subtitle">Device configuration</h5>
<div class="form-group">
<label class="form-switch">
<input type="checkbox" @change="switch_config('frontend', 'kiosk_mode')" v-model="config.frontend.kiosk_mode">
<i class="form-icon"></i> Use TinyCheck in Kiosk-mode.
</label>
<label class="form-switch">
<input type="checkbox" @change="switch_config('frontend', 'virtual_keyboard')" v-model="config.frontend.virtual_keyboard">
<i class="form-icon"></i> Use virtual keyboard (for touch screen)
</label>
<label class="form-switch">
<input type="checkbox" @change="switch_config('frontend', 'hide_mouse')" v-model="config.frontend.hide_mouse">
<i class="form-icon"></i> Hide mouse (for touch screen)
</label>
<label class="form-switch">
<input type="checkbox" @change="switch_config('network', 'tokenized_ssids')" v-model="config.network.tokenized_ssids">
<i class="form-icon"></i> Use tokenized SSIDs (eg. [ssid-name]-[hex-str]).
</label>
<label class="form-switch">
<input type="checkbox" @change="switch_config('frontend', 'download_links')" v-model="config.frontend.download_links">
<i class="form-icon"></i> Use in-browser download for network captures.
</label>
<label class="form-switch">
<input type="checkbox" @change="switch_config('frontend', 'sparklines')" v-model="config.frontend.sparklines">
<i class="form-icon"></i> Show background sparklines during the capture.
</label>
<label class="form-switch">
<input type="checkbox" @change="switch_config('frontend', 'remote_access')" v-model="config.frontend.remote_access">
<i class="form-icon"></i> Allow remote access to the frontend.
</label>
<label class="form-switch">
<input type="checkbox" @change="switch_config('backend', 'remote_access')" v-model="config.backend.remote_access">
<i class="form-icon"></i> Allow remote access to the backend.
</label>
</div>
<h5 class="s-subtitle">Analysis configuration</h5>
<div class="form-group">
<label class="form-switch">
<input type="checkbox" @change="local_analysis('analysis', 'heuristics')" v-model="config.analysis.heuristics">
<i class="form-icon"></i> Use heuristic detection for suspect behaviour.
</label>
<label class="form-switch">
<input type="checkbox" @change="local_analysis('analysis', 'iocs')" v-model="config.analysis.iocs">
<i class="form-icon"></i> Use Indicator of Compromise (IoC) based detection.
</label>
<label class="form-switch">
<input type="checkbox" @change="local_analysis('analysis', 'whitelist')" v-model="config.analysis.whitelist">
<i class="form-icon"></i> Use whitelist to prevent false positives.
</label>
</div>
<h5 class="s-subtitle">User credentials</h5>
<div class="form-group">
<div class="column col-10 col-xs-12">
<div class="form-group">
<label class="form-label" for="user-login">User login</label>
<div class="input-group">
<input class="form-input" id="user-login" type="text" v-model="config.backend.login">
<button class="btn btn-primary input-group-btn px150" @click="change_login()">Update it</button>
</div>
</div>
<div class="form-group">
<label class="form-label" for="user-login">User password</label>
<div class="input-group">
<input class="form-input" id="user-login" type="password" placeholder="●●●●●●" v-model="config.backend.password">
<button class="btn btn-primary input-group-btn px150" @click="change_password()">Update it</button>
</div>
</div>
<div class="whitespace"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'edit-configuration',
data() {
return {
config: {},
check_certificate: false,
certificate: "",
toaster: { show: false, message : "", type : null }
}
},
props: {},
methods: {
switch_config: function(cat, key) {
axios.get(`/api/config/switch/${cat}/${key}`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data.status) {
if (response.data.message == "Key switched to true") {
this.toaster = { show : true, message : "Configuration updated", type : "success" }
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
this.config[cat][key] = true
} else if (response.data.message == "Key switched to false") {
this.toaster = { show : true, message : "Configuration updated", type : "success" }
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
this.config[cat][key] = false
} else {
this.toaster = { show : true, message : "The key doesn't exist", type : "error" }
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
}
}
})
.catch(err => (console.log(err)))
},
load_config: function() {
axios.get(`/api/config/list`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data) {
this.config = response.data
this.config.backend.password = ""
}
})
.catch(err => (console.log(err)))
},
async get_jwt() {
await axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if (response.data.token) {
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
},
local_analysis: function(cat, key) {
this.switch_config(cat, key);
if (this.config.analysis.remote != false)
this.switch_config("analysis", "remote");
},
change_login: function() {
axios.get(`/api/config/edit/backend/login/${this.config.backend.login}`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data.status) {
this.toaster = { show : true, message : "Login changed", type : "success" }
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
} else {
this.toaster = { show : true, message : "Login not changed", type : "error" }
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
}
})
.catch(err => (console.log(err)))
},
change_password: function() {
axios.get(`/api/config/edit/backend/password/${this.config.backend.password}`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data.status) {
this.toaster = { show : true, message : "Password changed", type : "success" }
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
} else {
this.toaster = { show : true, message : "Password not changed", type : "error" }
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
}
})
.catch(err => (console.log(err)))
}
},
created: function() {
this.get_jwt().then(() => {
this.load_config();
});
}
}
</script>

View File

@ -0,0 +1,19 @@
<template>
<div class="backend-content" id="content">
<div class="column col-6 col-xs-12">
<div class="container">
<h3 class="s-title">Getting started with TinyCheck</h3>
<img src="@/assets/network-home.png" id="network-thumbnail" />
<p>TinyCheck allows you to capture easily internet communications from a smartphone or any device which can be associated to a Wi-Fi access point in order to quickly analyze and save them.
This can be used to verify if there is any suspect or malicious communication from a smartphone, by using heuristics or specific Indicators of Compromise (IoCs).</p>
<p>This little backend allows you to manage the configuration of your TinyCheck instance. You can push some IOCs for detection and whitelist elements which can be seen during legit communications in order to prevent false positives.</p>
</div>
<div class="backend-footer container grid-lg" id="copyright">
<p>Created with <span class="text-error">&hearts;</span> by <a href="https://twitter.com/felixaime" target="_blank">Félix Aimé</a>.</p>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,254 @@
<template>
<div class="backend-content" id="content">
<div class="column col-6 col-xs-12">
<h3 class="s-title">Manage IOCs</h3>
<ul class="tab tab-block">
<li class="tab-item">
<a href="#" v-on:click="switch_tab('bulk')" v-bind:class="{ active: tabs.bulk }">Bulk import</a>
</li>
<li class="tab-item">
<a href="#" v-on:click="switch_tab('file')" v-bind:class="{ active: tabs.file }">File import</a>
</li>
<li class="tab-item">
<a href="#" v-on:click="switch_tab('export')" v-bind:class="{ active: tabs.export }">Export IOCs</a>
</li>
</ul>
<div v-if="tabs.export">
<iframe :src="export_url" class="frame-export"></iframe>
</div>
<div v-if="tabs.file">
<label class="form-upload empty" for="upload">
<input type="file" class="upload-field" id="upload" @change="import_from_file">
<p class="empty-title h5">Drop or select a file to import.</p>
<p class="empty-subtitle">The file needs to be an export from a TinyCheck instance.</p>
</label>
</div>
<div v-if="tabs.bulk">
<div class="columns">
<div class="column col-4 col-xs-4">
<div class="form-group">
<select class="form-select" placeholder="test" v-model="tag">
<option value="">IOC(s) Tag</option>
<option v-for="t in tags" :value="t.tag" :key="t.tag">
{{ t.name }}
</option>
</select>
</div>
</div>
<div class="column col-4 col-xs-4">
<div class="form-group">
<select class="form-select width-full" placeholder="test" v-model="type">
<option value="">IOC(s) Type</option>
<option value="unknown">Multiple (regex parsing)</option>
<option v-for="t in types" :value="t.type" :key="t.type">
{{ t.name }}
</option>
</select>
</div>
</div>
<div class="column col-4 col-xs-4">
<div class="form-group">
<select class="form-select width-full" placeholder="test" v-model="tlp">
<option value="">IOC(s) TLP</option>
<option value="white">TLP:WHITE</option>
<option value="green">TLP:GREEN</option>
<option value="amber">TLP:AMBER</option>
<option value="red">TLP:RED</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<textarea class="form-input" id="input-example-3" placeholder="Paste your Indicators of Compromise here" rows="15" v-model="iocs"></textarea>
</div>
<div class="form-group">
<button class="btn-primary btn col-12" v-on:click="import_from_bulk()">Import the IOCs</button>
</div>
</div>
<div class="form-group" v-if="imported.length>0">
<div class="toast toast-success">
{{imported.length}} IOC<span v-if="errors.length>1">s</span> imported successfully.
</div>
</div>
<div v-if="errors.length>0">
<div class="form-group">
<div class="toast toast-error">
{{errors.length}} IOC<span v-if="errors.length>1">s</span> not imported, see details below.
</div>
</div>
<div class="form-group">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Indicator</th>
<th>Importation error</th>
</tr>
</thead>
<tbody>
<tr v-for="e in errors" v-bind:key="e.ioc">
<td>{{ e.ioc }}</td>
<td>{{ e.message }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-else-if="type_tag_error==true">
<div class="form-group">
<div class="toast toast-error">
IOC(s) not imported, see details below.
</div>
</div>
<div class="form-group">
<div class="empty">
<p class="empty-title h5">Please select a tag and a type.</p>
<p class="empty-subtitle">If different IOCs types, select "Unknown (regex parsing)".</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'manageiocs',
data() {
return {
type:"",
tag:"",
tlp:"",
iocs:"",
types:[],
tags:[],
errors:[],
imported:[],
type_tag_error: false,
wrong_ioc_file: false,
tabs: { "bulk" : true, "file" : false, "export" : false },
jwt:"",
export_url:"",
config: {},
watcher: ""
}
},
props: { },
methods: {
import_from_bulk: function() {
this.errors = []
this.imported = []
if (this.tag != "" && this.type != "" && this.tlp != ""){
this.iocs.match(/[^\r\n]+/g).forEach(ioc => {
this.import_ioc(this.tag, this.type, this.tlp, ioc);
});
this.iocs = "";
} else {
this.type_tag_error = true
}
},
import_ioc: function(tag, type, tlp, ioc) {
if (ioc != "" && ioc.slice(0,1) != "#"){
if("alert " != ioc.slice(0,6)) {
ioc = ioc.trim()
ioc = ioc.replace(" ", "")
ioc = ioc.replace("[", "")
ioc = ioc.replace("]", "")
ioc = ioc.replace("\\", "")
ioc = ioc.replace("(", "")
ioc = ioc.replace(")", "")
}
axios.get(`/api/ioc/add/${type.trim()}/${tag.trim()}/${tlp.trim()}/${ioc}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.status){
this.imported.push(response.data);
} else if (response.data.message){
this.errors.push(response.data);
}
})
.catch(err => (console.log(err)))
}
},
delete_watcher: function(watcher) {
var i = this.config.watchers.indexOf(watcher);
this.config.watchers.splice(i, 1);
},
add_watcher: function() {
this.config.watchers.push(this.watcher);
this.watcher = "";
},
enrich_selects: function() {
axios.get(`/api/ioc/get/tags`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.tags) this.tags = response.data.tags
})
.catch(err => (console.log(err)));
axios.get(`/api/ioc/get/types`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.types) this.types = response.data.types
})
.catch(err => (console.log(err)));
},
switch_tab: function(tab) {
this.errors = []
this.imported = []
Object.keys(this.tabs).forEach(key => {
if( key == tab ){
this.tabs[key] = true
} else {
this.tabs[key] = false
}
});
},
import_from_file: function(ev) {
this.errors = []
this.imported = []
const file = ev.target.files[0];
const reader = new FileReader();
reader.onload = e => this.$emit("load", e.target.result);
reader.onload = () => {
try {
JSON.parse(reader.result).iocs.forEach(ioc => {
this.import_ioc(ioc["tag"], ioc["type"], ioc["tlp"], ioc["value"])
})
} catch (error) {
this.wrong_ioc_file = true
}
}
reader.readAsText(file);
},
async get_jwt(){
await axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if(response.data.token){
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
},
load_config: function() {
axios.get(`/api/config/list`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data) {
this.config = response.data
}
})
.catch(err => (console.log(err)))
}
},
created: function() {
this.get_jwt().then(() => {
this.enrich_selects();
this.load_config();
this.export_url = `/api/ioc/export?token=${this.jwt}`
});
}
}
</script>

View File

@ -0,0 +1,114 @@
<template>
<div class="backend-content" id="content">
<div class="column col-8 col-xs-12">
<h3 class="s-title">Search IOCs</h3>
<div class="form-group">
<textarea class="form-input" id="input-example-3" placeholder="Paste your IOCs here" rows="3" v-model="iocs"></textarea>
</div>
<div class="form-group">
<button class="btn btn-primary col-12" v-on:click="search_iocs()">Search</button>
</div>
<div class="form-group" v-if="results.length>0 ">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Indicator</th>
<th>Type</th>
<th>Tag</th>
<th>TLP</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr v-for="r in results" :key="r.tlp">
<td>{{ r.value }}</td>
<td class="capi">{{ r.type }}</td>
<td class="upper">{{ r.tag }}</td>
<td><label :class="['tlp-' + r.tlp]">{{ r.tlp }}</label></td>
<td><button class="btn btn-sm" v-on:click="remove(r)">Delete</button></td>
</tr>
</tbody>
</table>
</div>
<div v-else-if="first_search==false">
<div class="empty">
<p class="empty-title h5">IOC<span v-if="this.iocs.match(/[^\r\n]+/g).length>1">s</span> not found.</p>
<p class="empty-subtitle">Try wildcard search to expend your search.</p>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'iocs-search',
data() {
return {
results: [],
first_search: true,
jwt:""
}
},
props: { },
methods: {
search_iocs: function() {
this.results = []
this.first_search = false
this.iocs.match(/[^\r\n]+/g).forEach(ioc => {
ioc = ioc.trim()
if("alert " != ioc.slice(0,6)) {
ioc = ioc.replace(" ", "")
ioc = ioc.replace("[", "")
ioc = ioc.replace("]", "")
ioc = ioc.replace("\\", "")
ioc = ioc.replace("(", "")
ioc = ioc.replace(")", "")
}
axios.get(`/api/ioc/search/${ioc}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.results.length>0){
this.results = [].concat(this.results, response.data.results);
}
})
.catch(err => (console.log(err)))
});
return true;
},
remove: function(elem){
axios.get(`/api/ioc/delete/${elem.id}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.status){
this.results = this.results.filter(function(el) { return el != elem; });
}
})
.catch(err => (console.log(err)))
},
load_config: function() {
axios.get(`/api/config/list`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data) {
this.config = response.data
}
})
.catch(err => (console.log(err)))
},
async get_jwt(){
await axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if(response.data.token){
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
}
},
created: function() {
this.get_jwt()
}
}
</script>

View File

@ -0,0 +1,116 @@
<template>
<div class="backend-content" id="content">
<div class="column col-6 col-xs-12">
<h3 class="s-title">Network configuration</h3>
<h5 class="s-subtitle">Interfaces configuration</h5>
<img src="@/assets/network.png" id="network-thumbnail" />
<div class="container interfaces-container">
<div class="columns">
<div class="column col-6">
<span class="interface-label">First interface</span>
<div class="form-group">
<div class="btn-group btn-group-block">
<button class="btn btn-sm btn-iface" @click="change_interface('in', iface)" :class="iface == config.network.in ? 'active' : ''" v-for="iface in config.interfaces" :key="iface">{{ iface.toUpperCase() }}</button>
</div>
</div>
</div>
<div class="column col-6">
<span class="interface-label">Second interface</span>
<div class="form-group">
<div class="btn-group btn-group-block">
<button class="btn btn-sm btn-iface" @click="change_interface('out', iface)" :class="iface == config.network.out ? 'active' : ''" v-for="iface in config.interfaces" :key="iface">{{ iface.toUpperCase() }}</button>
</div>
</div>
</div>
</div>
</div>
<h5 class="s-subtitle">Edit SSIDs names</h5>
<div class="form-group">
<table class="table table-striped table-hover">
<tbody>
<tr v-for="ssid in config.network.ssids" :key="ssid">
<td>{{ ssid }}</td>
<td><button class="btn btn-sm" v-on:click="delete_ssid(ssid)">Delete</button></td>
</tr>
<tr>
<td><input class="form-input" v-model="ssid" type="text" placeholder="SSID name"></td>
<td><button class="btn btn-sm" @click="add_ssid()">Add</button></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'manageinterface',
data() {
return {
config: {},
ssid: ""
}
},
props: {},
methods: {
async get_jwt() {
await axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if (response.data.token) {
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
},
load_config: function() {
axios.get(`/api/config/list`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data) {
this.config = response.data
}
})
.catch(err => (console.log(err)))
},
delete_ssid: function(ssid) {
var i = this.config.network.ssids.indexOf(ssid);
this.config.network.ssids.splice(i, 1);
this.update_ssids();
},
add_ssid: function() {
this.config.network.ssids.push(this.ssid);
this.ssid = "";
this.update_ssids();
},
update_ssids: function() {
axios.get(`/api/config/edit/network/ssids/${this.config.network.ssids.join("|")}`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data.status) {
console.log(response.data)
}
})
.catch(err => (console.log(err)))
},
change_interface: function(type, iface) {
axios.get(`/api/config/edit/network/${type}/${iface}`, {
timeout: 10000,
headers: { 'X-Token': this.jwt }
}).then(response => {
if (response.data.status) this.config.network[type] = iface
})
.catch(err => (console.log(err)))
},
},
created: function() {
this.get_jwt().then(() => {
this.load_config();
});
}
}
</script>

View File

@ -0,0 +1,191 @@
<template>
<div class="backend-content" id="content">
<div class="column col-6 col-xs-12">
<h3 class="s-title">Manage whitelisted elements</h3>
<ul class="tab tab-block">
<li class="tab-item">
<a href="#" v-on:click="switch_tab('bulk')" v-bind:class="{ active: tabs.bulk }">Bulk elements import</a>
</li>
<li class="tab-item">
<a href="#" v-on:click="switch_tab('file')" v-bind:class="{ active: tabs.file }">Import from file</a>
</li>
<li class="tab-item">
<a href="#" v-on:click="switch_tab('export')" v-bind:class="{ active: tabs.export }">Export elements</a>
</li>
</ul>
<div v-if="tabs.export">
<iframe :src="export_url" class="frame-export"></iframe>
</div>
<div v-if="tabs.file">
<label class="form-upload empty" for="upload">
<input type="file" class="upload-field" id="upload" @change="import_from_file">
<p class="empty-title h5">Drop or select a file to import.</p>
<p class="empty-subtitle">The file needs to be an whitelist file export from a TinyCheck instance.</p>
</label>
</div>
<div v-if="tabs.bulk">
<div class="form-group">
<select class="form-select width-full" placeholder="test" v-model="type">
<option value="">Elements Type</option>
<option value="unknown">Multiple (regex parsing)</option>
<option v-for="t in types" :value="t.type" :key="t.type">
{{ t.name }}
</option>
</select>
</div>
<div class="form-group">
<textarea class="form-input" id="input-example-3" placeholder="Paste the elements to be whitelisted here" rows="15" v-model="elements"></textarea>
</div>
<div class="form-group">
<button class="btn-primary btn col-12" v-on:click="import_from_bulk()">Whitelist elements</button>
</div>
</div>
<div class="form-group" v-if="imported.length>0">
<div class="toast toast-success">
{{imported.length}} IOC<span v-if="errors.length>1">s</span> imported successfully.
</div>
</div>
<div v-if="errors.length>0">
<div class="form-group">
<div class="toast toast-error">
{{errors.length}} IOC<span v-if="errors.length>1">s</span> not imported, see details below.
</div>
</div>
<div class="form-group">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Element</th>
<th>Importation error</th>
</tr>
</thead>
<tbody>
<tr v-for="e in errors" :key="e.element">
<td>{{ e.element }}</td>
<td>{{ e.message }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-else-if="type_tag_error==true">
<div class="form-group">
<div class="toast toast-error">
IOC(s) not imported, see details below.
</div>
</div>
<div class="form-group">
<div class="empty">
<p class="empty-title h5">Please select a tag and a type.</p>
<p class="empty-subtitle">If different IOCs types, select "Unknown (regex parsing)".</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'manageiocs',
data() {
return {
type:"",
elements:"",
types:[],
errors:[],
imported:[],
wrong_wh_file: false,
tabs: { "bulk" : true, "file" : false, "export" : false },
jwt:"",
export_url:""
}
},
props: { },
methods: {
import_from_bulk: function() {
this.errors = []
this.imported = []
if (this.type != ""){
this.elements.match(/[^\r\n]+/g).forEach(elem => {
this.import_element(this.type, elem);
});
this.elements = "";
} else {
this.type_tag_error = true
}
},
import_element: function(type, elem) {
if (elem != "" && elem.slice(0,1) != "#"){
axios.get(`/api/whitelist/add/${type.trim()}/${elem.trim()}`, {
timeout: 10000,
headers: { "X-Token" : this.jwt }
}).then(response => {
if(response.data.status){
this.imported.push(response.data);
} else if (response.data.message){
this.errors.push(response.data);
}
})
.catch(err => (console.log(err)))
}
},
enrich_types: function() {
axios.get(`/api/whitelist/get/types`, { timeout: 10000, headers: {'X-Token': this.jwt} })
.then(response => {
if(response.data.types) this.types = response.data.types
})
.catch(err => (console.log(err)));
},
switch_tab: function(tab) {
this.errors = []
this.imported = []
Object.keys(this.tabs).forEach(key => {
if( key == tab ){
this.tabs[key] = true
} else {
this.tabs[key] = false
}
});
},
import_from_file: function(ev) {
this.errors = []
this.imported = []
const file = ev.target.files[0];
const reader = new FileReader();
reader.onload = e => this.$emit("load", e.target.result);
reader.onload = () => {
try {
JSON.parse(reader.result).elements.forEach(elem => {
this.import_element(elem["type"], elem["element"])
})
} catch (error) {
this.wrong_wh_file = true
}
}
reader.readAsText(file);
},
async get_jwt(){
await axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if(response.data.token){
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
}
},
created: function() {
this.get_jwt().then(() => {
this.enrich_types();
this.export_url = `/api/whitelist/export?token=${this.jwt}`
});
}
}
</script>

View File

@ -0,0 +1,94 @@
<template>
<div class="backend-content" id="content">
<div class="column col-6 col-xs-12">
<h3 class="s-title">Search whitelisted elements</h3>
<div class="form-group">
<textarea class="form-input" id="input-example-3" placeholder="Paste the elements here" rows="3" v-model="elements"></textarea>
</div>
<div class="form-group">
<button class="btn btn-primary col-12" v-on:click="search_elements()">Search</button>
</div>
<div class="form-group" v-if="results.length>0 ">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Element</th>
<th>Element type</th>
<th> </th>
</tr>
</thead>
<tbody>
<tr v-for="r in results" :key="r.element">
<td>{{ r.element }}</td>
<td>{{ r.type }}</td>
<td><button class="btn btn-sm" v-on:click="remove(r)">Delete</button></td>
</tr>
</tbody>
</table>
</div>
<div v-else-if="first_search==false">
<div class="empty">
<p class="empty-title h5">Element<span v-if="this.elements.match(/[^\r\n]+/g).length>1">s</span> not found.</p>
<p class="empty-subtitle">Try wildcard search to expend your search.</p>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'elements-search',
data() {
return {
results: [],
first_search: true,
jwt:""
}
},
props: { },
methods: {
search_elements: function() {
this.results = []
this.first_search = false
this.elements.match(/[^\r\n]+/g).forEach(elem => {
axios.get(`/api/whitelist/search/${elem.trim()}`, {
timeout: 10000,
headers: {'X-Token': this.jwt}
}).then(response => {
if(response.data.results.length>0){
this.results = [].concat(this.results, response.data.results);
}
})
.catch(err => (console.log(err)))
});
return true;
},
remove: function(elem){
axios.get(`/api/whitelist/delete/${elem.id}`, {
timeout: 10000,
headers: {'X-Token': this.jwt}
}).then(response => {
if(response.data.status){
this.results = this.results.filter(function(el) { return el != elem; });
}
})
.catch(err => (console.log(err)))
},
async get_jwt(){
await axios.get(`/api/get-token`, { timeout: 10000 })
.then(response => {
if(response.data.token){
this.jwt = response.data.token
}
})
.catch(err => (console.log(err)))
}
},
created: function() {
this.get_jwt()
}
}
</script>