First commit!
@@ -0,0 +1,105 @@
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and *not* Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
@@ -0,0 +1 @@
|
||||
timeout=60000
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 805 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>@felixaime/spyguard-backend</title><link href="/js/chunk-2d0a3726.bebe5116.js" rel="prefetch"><link href="/js/chunk-2d0c0c86.cf098d4b.js" rel="prefetch"><link href="/js/chunk-2d0c8321.78ca1407.js" rel="prefetch"><link href="/js/chunk-2d0c9378.fb16a6a2.js" rel="prefetch"><link href="/js/chunk-2d0cc2b4.6cd43049.js" rel="prefetch"><link href="/js/chunk-2d0cfa15.4b2166d6.js" rel="prefetch"><link href="/js/chunk-2d0e4bf5.fac73388.js" rel="prefetch"><link href="/js/chunk-2d20faf8.c983e105.js" rel="prefetch"><link href="/js/chunk-2d215cd3.f0d8ba06.js" rel="prefetch"><link href="/js/chunk-2d2245cf.ea4d33bd.js" rel="prefetch"><link href="/js/chunk-2d22d432.91ebbe55.js" rel="prefetch"><link href="/js/chunk-3c0d118e.e3f2944c.js" rel="prefetch"><link href="/css/app.b563f628.css" rel="preload" as="style"><link href="/js/app.a3c6f292.js" rel="preload" as="script"><link href="/js/chunk-vendors.dbaccf43.js" rel="preload" as="script"><link href="/css/app.b563f628.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but @felixaime/spyguard-backend doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.dbaccf43.js"></script><script src="/js/app.a3c6f292.js"></script></body></html>
|
||||
@@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0a3726"],{"01e4":function(e,t,n){"use strict";n.r(t);var a=function(){var e=this,t=e._self._c;return t("div",{staticClass:"backend-content",attrs:{id:"content"}},[t("div",{staticClass:"column col-8 col-xs-12"},[t("br"),t("p",[t("strong",[e._v("A new SpyGuard version is available ("+e._s(e.next_version)+").")]),t("br"),e.update_launched?e._e():t("span",[e._v("Please click on the button below to update your instance.")]),e.update_launched&&!e.update_finished?t("span",[e._v("Updating SpyGuard, please wait. You'll be redirected once updated.")]):e._e(),e.update_launched&&e.update_finished?t("span",{staticClass:"color-green"},[e._v("✓ Update finished!")]):e._e()]),e.update_finished?e._e():t("button",{staticClass:"btn btn-primary",class:[e.update_launched?"loading":""],on:{click:function(t){return e.launch_update()}}},[e._v("Update Spyguard")])])])},s=[],i=n("bc3a"),o=n.n(i),c={name:"update",data(){return{translation:{},update_launched:null,check_interval:null,next_version:null,current_version:null,update_finished:!1,jwt:""}},methods:{check_version:function(){o.a.get("/api/update/get-version",{timeout:6e4,headers:{"X-Token":this.jwt}}).then(e=>{e.data.status&&e.data.current_version==this.next_version&&(window.current_version=e.data.current_version,this.update_finished=!0,clearInterval(this.check_interval),setTimeout((function(){window.location.href="/"}),3e3))}).catch(e=>{console.log(e)})},launch_update:function(){o.a.get("/api/update/process",{timeout:6e4,headers:{"X-Token":this.jwt}}).then(e=>{e.data.status&&"Update successfully launched"==e.data.message&&(this.update_launched=!0,this.check_interval=setInterval(function(){this.check_version()}.bind(this),3e3))}).catch(e=>{console.log(e)})},async get_jwt(){await o.a.get("/api/get-token",{timeout:1e4}).then(e=>{e.data.token&&(this.jwt=e.data.token)}).catch(e=>console.log(e))},get_versions:function(){o.a.get("/api/update/check",{timeout:6e4,headers:{"X-Token":this.jwt}}).then(e=>{e.data.status&&(this.current_version=e.data.current_version,this.next_version=e.data.next_version,this.current_version==this.next_version&&(window.location.href="/"))}).catch(e=>{console.log(e)})}},created:function(){this.get_jwt().then(()=>{this.get_versions()})}},u=c,r=n("2877"),d=Object(r["a"])(u,a,s,!1,null,null,null);t["default"]=d.exports}}]);
|
||||
//# sourceMappingURL=chunk-2d0a3726.bebe5116.js.map
|
||||
@@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0cc2b4"],{"4d7e":function(t,s,e){"use strict";e.r(s);var a=function(){var t=this,s=t._self._c;return s("div",{staticClass:"backend-content",attrs:{id:"content"}},[s("div",{staticClass:"column col-12 col-xs-12"},[s("h3",{staticClass:"s-title"},[t._v("Search IOCs")]),s("div",{staticClass:"form-group"},[s("textarea",{directives:[{name:"model",rawName:"v-model",value:t.iocs,expression:"iocs"}],staticClass:"form-input",attrs:{id:"input-example-3",placeholder:"Paste your IOCs here",rows:"3"},domProps:{value:t.iocs},on:{input:function(s){s.target.composing||(t.iocs=s.target.value)}}})]),s("div",{staticClass:"form-group"},[s("button",{staticClass:"btn btn-primary col-12",on:{click:function(s){return t.search_iocs()}}},[t._v("Search")])]),t.results.length>0?s("div",{staticClass:"form-group"},[s("table",{staticClass:"table table-striped table-hover"},[t._m(0),s("tbody",t._l(t.results,(function(e){return s("tr",{key:e.tlp},[s("td",[t._v(t._s(e.value))]),s("td",{staticClass:"upper"},[t._v(t._s(e.tag))]),s("td",[s("label",{class:["tlp-"+e.tlp]},[t._v(t._s(e.tlp))])]),s("td",{staticClass:"capi"},[t._v(t._s(e.source))]),s("td",[s("button",{staticClass:"btn btn-sm",on:{click:function(s){return t.remove(e)}}},[t._v("Delete")])])])})),0)])]):0==t.first_search?s("div",[t.loading?s("div",[t._m(1)]):s("div",[s("div",{staticClass:"empty"},[s("p",{staticClass:"empty-title h5"},[t._v("IOC"),this.iocs.match(/[^\r\n]+/g).length>1?s("span",[t._v("s")]):t._e(),t._v(" not found.")]),s("p",{staticClass:"empty-subtitle"},[t._v("Try wildcard search to expend your search.")])])])]):t._e()])])},i=[function(){var t=this,s=t._self._c;return s("thead",[s("tr",[s("th",[t._v("Indicator")]),s("th",[t._v("Tag")]),s("th",[t._v("TLP")]),s("th",[t._v("Source")]),s("th",[t._v("Action")])])])},function(){var t=this,s=t._self._c;return s("div",{staticClass:"empty"},[s("p",{staticClass:"empty-title h5"},[s("span",{staticClass:"loading loading-lg"})]),s("p",{staticClass:"empty-subtitle"},[t._v("Finding your IOC(s)...")])])}],c=e("bc3a"),o=e.n(c),n={name:"iocs-search",data(){return{results:[],first_search:!0,jwt:"",loading:!1}},props:{},methods:{search_iocs:function(){return this.results=[],this.first_search=!1,this.loading=!0,this.iocs.match(/[^\r\n]+/g).forEach(t=>{t=t.trim(),"alert "!=t.slice(0,6)&&(t=t.replace(" ",""),t=t.replace("[",""),t=t.replace("]",""),t=t.replace("\\",""),t=t.replace("(",""),t=t.replace(")","")),o.a.get("/api/ioc/search/"+t,{timeout:1e4,headers:{"X-Token":this.jwt}}).then(t=>{t.data.results.length>0&&(this.results=[].concat(this.results,t.data.results)),this.loading=!1}).catch(t=>console.log(t))}),!0},remove:function(t){o.a.get("/api/ioc/delete/"+t.id,{timeout:1e4,headers:{"X-Token":this.jwt}}).then(s=>{s.data.status&&(this.results=this.results.filter((function(s){return s!=t})))}).catch(t=>console.log(t))},load_config:function(){o.a.get("/api/config/list",{timeout:1e4,headers:{"X-Token":this.jwt}}).then(t=>{t.data&&(this.config=t.data)}).catch(t=>console.log(t))},async get_jwt(){await o.a.get("/api/get-token",{timeout:1e4}).then(t=>{t.data.token&&(this.jwt=t.data.token)}).catch(t=>console.log(t))}},created:function(){this.get_jwt()}},l=n,r=e("2877"),u=Object(r["a"])(l,a,i,!1,null,null,null);s["default"]=u.exports}}]);
|
||||
//# sourceMappingURL=chunk-2d0cc2b4.6cd43049.js.map
|
||||
@@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0cfa15"],{6511:function(t,s,e){"use strict";e.r(s);var a=function(){var t=this;t._self._c;return t._m(0)},i=[function(){var t=this,s=t._self._c;return s("div",{staticClass:"backend-content",attrs:{id:"content"}},[s("div",{staticClass:"column col-8 col-xs-12"},[s("div",{staticClass:"container"},[s("h3",{staticClass:"s-title"},[t._v("Getting started")]),s("br"),s("p",[t._v("SpyGuard is a forked and enhanced version of TinyCheck, an application developed by Kaspersky. SpyGuard's main objective is to detect signs of compromise by monitoring network flows transmitted by a device.")]),s("p",[t._v("As it uses WiFi, SpyGuard can be used against a wide range of devices, such as smartphones, laptops, IOTs or workstations. To do its job, the analysis engine of SpyGuard is using Indicators of Compromise (IOCs), anomaly detection and is supported by Suricata. ")]),s("p",[t._v("This backend lets you configure your SpyGuard instance. You can push some IOCs for detection and whitelist elements which can be seen during legit communications in order to prevent false positives.")]),s("p",[t._v("_")])]),s("div",{staticClass:"backend-footer container grid-lg",attrs:{id:"copyright"}},[s("p",[t._v("For any question, bug report or feedback, please contact the "),s("a",{attrs:{href:"mailto:spyguard@protonmail.com",target:"_blank"}},[t._v("SpyGuard's Team")]),t._v(" or open an issue on the "),s("a",{attrs:{href:"https://github.com/SpyGuard/spyguard/issues",target:"_blank"}},[t._v("SpyGuard Github repository")]),t._v(".")])])])])}],n=e("2877"),o={},r=Object(n["a"])(o,a,i,!1,null,null,null);s["default"]=r.exports}}]);
|
||||
//# sourceMappingURL=chunk-2d0cfa15.4b2166d6.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack:///./src/views/home.vue","webpack:///./src/views/home.vue?46ae"],"names":["render","_vm","this","_self","_c","_m","staticRenderFns","staticClass","attrs","_v","script","component"],"mappings":"uHAAA,IAAIA,EAAS,WAAkB,IAAIC,EAAIC,KAAQD,EAAIE,MAAMC,GAAG,OAAOH,EAAII,GAAG,IAEtEC,EAAkB,CAAC,WAAY,IAAIL,EAAIC,KAAKE,EAAGH,EAAIE,MAAMC,GAAG,OAAOA,EAAG,MAAM,CAACG,YAAY,kBAAkBC,MAAM,CAAC,GAAK,YAAY,CAACJ,EAAG,MAAM,CAACG,YAAY,0BAA0B,CAACH,EAAG,MAAM,CAACG,YAAY,aAAa,CAACH,EAAG,KAAK,CAACG,YAAY,WAAW,CAACN,EAAIQ,GAAG,qBAAqBL,EAAG,MAAMA,EAAG,IAAI,CAACH,EAAIQ,GAAG,oNAAoNL,EAAG,IAAI,CAACH,EAAIQ,GAAG,0QAA0QL,EAAG,IAAI,CAACH,EAAIQ,GAAG,4MAA4ML,EAAG,IAAI,CAACH,EAAIQ,GAAG,SAASL,EAAG,MAAM,CAACG,YAAY,mCAAmCC,MAAM,CAAC,GAAK,cAAc,CAACJ,EAAG,IAAI,CAACH,EAAIQ,GAAG,iEAAiEL,EAAG,IAAI,CAACI,MAAM,CAAC,KAAO,iCAAiC,OAAS,WAAW,CAACP,EAAIQ,GAAG,qBAAqBR,EAAIQ,GAAG,6BAA6BL,EAAG,IAAI,CAACI,MAAM,CAAC,KAAO,8CAA8C,OAAS,WAAW,CAACP,EAAIQ,GAAG,gCAAgCR,EAAIQ,GAAG,e,YCDr8CC,EAAS,GAKTC,EAAY,eACdD,EACAV,EACAM,GACA,EACA,KACA,KACA,MAIa,aAAAK,E","file":"js/chunk-2d0cfa15.4b2166d6.js","sourcesContent":["var render = function render(){var _vm=this,_c=_vm._self._c;return _vm._m(0)\n}\nvar staticRenderFns = [function (){var _vm=this,_c=_vm._self._c;return _c('div',{staticClass:\"backend-content\",attrs:{\"id\":\"content\"}},[_c('div',{staticClass:\"column col-8 col-xs-12\"},[_c('div',{staticClass:\"container\"},[_c('h3',{staticClass:\"s-title\"},[_vm._v(\"Getting started\")]),_c('br'),_c('p',[_vm._v(\"SpyGuard is a forked and enhanced version of TinyCheck, an application developed by Kaspersky. SpyGuard's main objective is to detect signs of compromise by monitoring network flows transmitted by a device.\")]),_c('p',[_vm._v(\"As it uses WiFi, SpyGuard can be used against a wide range of devices, such as smartphones, laptops, IOTs or workstations. To do its job, the analysis engine of SpyGuard is using Indicators of Compromise (IOCs), anomaly detection and is supported by Suricata. \")]),_c('p',[_vm._v(\"This backend lets you configure your SpyGuard instance. You can push some IOCs for detection and whitelist elements which can be seen during legit communications in order to prevent false positives.\")]),_c('p',[_vm._v(\"_\")])]),_c('div',{staticClass:\"backend-footer container grid-lg\",attrs:{\"id\":\"copyright\"}},[_c('p',[_vm._v(\"For any question, bug report or feedback, please contact the \"),_c('a',{attrs:{\"href\":\"mailto:spyguard@protonmail.com\",\"target\":\"_blank\"}},[_vm._v(\"SpyGuard's Team\")]),_vm._v(\" or open an issue on the \"),_c('a',{attrs:{\"href\":\"https://github.com/SpyGuard/spyguard/issues\",\"target\":\"_blank\"}},[_vm._v(\"SpyGuard Github repository\")]),_vm._v(\".\")])])])])\n}]\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./home.vue?vue&type=template&id=34abb80e&\"\nvar script = {}\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports"],"sourceRoot":""}
|
||||
@@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0e4bf5"],{9217:function(t,a,e){"use strict";e.r(a);var s=function(){var t=this,a=t._self._c;return a("div",{staticClass:"backend-content",attrs:{id:"content"}},[a("div",{staticClass:"column col-8 col-xs-12"},[a("h3",{staticClass:"s-title"},[t._v("Manage database")]),a("ul",{staticClass:"tab tab-block"},[a("li",{staticClass:"tab-item"},[a("a",{class:{active:t.tabs.import},attrs:{href:"#"},on:{click:function(a){return t.switch_tab("import")}}},[t._v("Import database")])]),a("li",{staticClass:"tab-item"},[a("a",{class:{active:t.tabs.export},attrs:{href:"#"},on:{click:function(a){return t.switch_tab("export")}}},[t._v("Export database")])])]),t.tabs.export?a("div",[a("iframe",{staticClass:"frame-export",attrs:{src:t.export_url}})]):t._e(),t.tabs.import?a("div",[a("label",{staticClass:"form-upload empty",attrs:{for:"upload"}},[a("input",{staticClass:"upload-field",attrs:{type:"file",id:"upload"},on:{change:t.import_from_file}}),a("p",{staticClass:"empty-title h5"},[t._v("Drop or select a database to import.")]),a("p",{staticClass:"empty-subtitle"},[t._v("The database needs to be an export from a SpyGuard instance.")])])]):t._e()])])},i=[],o=e("bc3a"),r=e.n(o),n={name:"db-manage",data(){return{tabs:{import:!0,export:!1},jwt:""}},props:{},methods:{switch_tab:function(t){Object.keys(this.tabs).forEach(a=>{this.tabs[a]=a==t})},import_from_file:function(t){var a=new FormData;a.append("file",t.target.files[0]),r.a.post("/api/config/db/import",a,{headers:{"Content-Type":"multipart/form-data","X-Token":this.jwt}})},async get_jwt(){await r.a.get("/api/get-token",{timeout:1e4}).then(t=>{t.data.token&&(this.jwt=t.data.token)}).catch(t=>console.log(t))}},created:function(){this.get_jwt().then(()=>{this.export_url="/api/config/db/export?token="+this.jwt})}},c=n,l=e("2877"),p=Object(l["a"])(c,s,i,!1,null,null,null);a["default"]=p.exports}}]);
|
||||
//# sourceMappingURL=chunk-2d0e4bf5.fac73388.js.map
|
||||
@@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d2245cf"],{e08f:function(t,e,s){"use strict";s.r(e);var a=function(){var t=this,e=t._self._c;return e("div",{staticClass:"backend-content",attrs:{id:"content"}},[e("div",{staticClass:"column col-12 col-xs-12"},[e("h3",{staticClass:"s-title"},[t._v("Search whitelisted elements")]),e("div",{staticClass:"form-group"},[e("textarea",{directives:[{name:"model",rawName:"v-model",value:t.elements,expression:"elements"}],staticClass:"form-input",attrs:{id:"input-example-3",placeholder:"Paste the elements here",rows:"3"},domProps:{value:t.elements},on:{input:function(e){e.target.composing||(t.elements=e.target.value)}}})]),e("div",{staticClass:"form-group"},[e("button",{staticClass:"btn btn-primary col-12",on:{click:function(e){return t.search_elements()}}},[t._v("Search")])]),t.results.length>0?e("div",{staticClass:"form-group"},[e("table",{staticClass:"table table-striped table-hover"},[t._m(0),e("tbody",t._l(t.results,(function(s){return e("tr",{key:s.element},[e("td",[t._v(t._s(s.element))]),e("td",[t._v(t._s(s.type))]),e("td",[e("button",{staticClass:"btn btn-sm",on:{click:function(e){return t.remove(s)}}},[t._v("Delete")])])])})),0)])]):0==t.first_search?e("div",[e("div",{staticClass:"empty"},[e("p",{staticClass:"empty-title h5"},[t._v("Element"),this.elements.match(/[^\r\n]+/g).length>1?e("span",[t._v("s")]):t._e(),t._v(" not found.")]),e("p",{staticClass:"empty-subtitle"},[t._v("Try wildcard search to expend your search.")])])]):t._e()])])},n=[function(){var t=this,e=t._self._c;return e("thead",[e("tr",[e("th",[t._v("Element")]),e("th",[t._v("Element type")]),e("th")])])}],l=s("bc3a"),r=s.n(l),i={name:"elements-search",data(){return{results:[],first_search:!0,jwt:""}},props:{},methods:{search_elements:function(){return this.results=[],this.first_search=!1,this.elements.match(/[^\r\n]+/g).forEach(t=>{r.a.get("/api/whitelist/search/"+t.trim(),{timeout:1e4,headers:{"X-Token":this.jwt}}).then(t=>{t.data.results.length>0&&(this.results=[].concat(this.results,t.data.results))}).catch(t=>console.log(t))}),!0},remove:function(t){r.a.get("/api/whitelist/delete/"+t.id,{timeout:1e4,headers:{"X-Token":this.jwt}}).then(e=>{e.data.status&&(this.results=this.results.filter((function(e){return e!=t})))}).catch(t=>console.log(t))},async get_jwt(){await r.a.get("/api/get-token",{timeout:1e4}).then(t=>{t.data.token&&(this.jwt=t.data.token)}).catch(t=>console.log(t))}},created:function(){this.get_jwt()}},c=i,o=s("2877"),h=Object(o["a"])(c,a,n,!1,null,null,null);e["default"]=h.exports}}]);
|
||||
//# sourceMappingURL=chunk-2d2245cf.ea4d33bd.js.map
|
||||
@@ -0,0 +1,2 @@
|
||||
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-3c0d118e"],{"42e0":function(t,e,s){t.exports=s.p+"img/network.33d31f9d.png"},"7e11":function(t,e,s){"use strict";s.r(e);var i=function(){var t=this,e=t._self._c;return e("div",{staticClass:"backend-content",attrs:{id:"content"}},[e("div",{class:{"alert-toaster-visible":t.toaster.show,"alert-toaster-hidden":!t.toaster.show}},[t._v(t._s(t.toaster.message))]),e("div",{staticClass:"column col-8 col-xs-12"},[e("h3",{staticClass:"s-title"},[t._v("Network configuration")]),e("h5",{staticClass:"s-subtitle"},[t._v("Interfaces configuration")]),e("img",{attrs:{src:s("42e0"),id:"network-thumbnail"}}),e("div",{staticClass:"container interfaces-container"},[e("div",{staticClass:"columns"},[e("div",{staticClass:"column col-6"},[e("span",{staticClass:"interface-label"},[t._v("Wireless AP interface")]),e("select",{directives:[{name:"model",rawName:"v-model",value:t.iface_in,expression:"iface_in"}],staticClass:"form-select width-full",on:{change:[function(e){var s=Array.prototype.filter.call(e.target.options,(function(t){return t.selected})).map((function(t){var e="_value"in t?t._value:t.value;return e}));t.iface_in=e.target.multiple?s:s[0]},function(e){return t.change_interface("in",t.iface_in)}]}},t._l(t.config.ifaces_in,(function(s){return e("option",{key:s,domProps:{value:s}},[t._v(" "+t._s(s.toUpperCase())+" ")])})),0)]),e("div",{staticClass:"column col-6"},[e("span",{staticClass:"interface-label"},[t._v("Internet link interface")]),e("select",{directives:[{name:"model",rawName:"v-model",value:t.iface_out,expression:"iface_out"}],staticClass:"form-select width-full",on:{change:[function(e){var s=Array.prototype.filter.call(e.target.options,(function(t){return t.selected})).map((function(t){var e="_value"in t?t._value:t.value;return e}));t.iface_out=e.target.multiple?s:s[0]},function(e){return t.change_interface("out",t.iface_out)}]}},t._l(t.config.ifaces_out,(function(s){return e("option",{key:s,domProps:{value:s}},[t._v(" "+t._s(s.toUpperCase())+" ")])})),0)])])]),e("h5",{staticClass:"s-subtitle"},[t._v("Edit SSIDs names")]),e("div",{staticClass:"form-group"},[e("table",{staticClass:"table table-striped table-hover"},[t._m(0),e("tbody",[t._l(t.config.network.ssids,(function(s){return e("tr",{key:s},[e("td",[t._v(t._s(s))]),e("td",[e("button",{staticClass:"btn btn-sm",on:{click:function(e){return t.delete_ssid(s)}}},[t._v("Delete")])])])})),e("tr",[e("td",[e("input",{directives:[{name:"model",rawName:"v-model",value:t.ssid,expression:"ssid"}],staticClass:"form-input",attrs:{type:"text",placeholder:"SSID name"},domProps:{value:t.ssid},on:{input:function(e){e.target.composing||(t.ssid=e.target.value)}}})]),e("td",[e("button",{staticClass:"btn btn-sm",on:{click:function(e){return t.add_ssid()}}},[t._v("Add")])])])],2)])])])])},n=[function(){var t=this,e=t._self._c;return e("thead",[e("tr",[e("th",[t._v("Network name")]),e("th",[t._v("Action")])])])}],a=(s("14d9"),s("bc3a")),o=s.n(a),c={name:"manageinterface",data(){return{config:{},ssid:"",iface_in:"",toaster:{show:!1,message:"",type:null}}},props:{},methods:{async get_jwt(){await o.a.get("/api/get-token",{timeout:1e4}).then(t=>{t.data.token&&(this.jwt=t.data.token)}).catch(t=>console.log(t))},load_config:function(){o.a.get("/api/config/list",{timeout:1e4,headers:{"X-Token":this.jwt}}).then(t=>{t.data&&(this.config=t.data,this.iface_in=this.config.network.in,this.iface_out=this.config.network.out,console.log(this.iface_in))}).catch(t=>console.log(t))},delete_ssid:function(t){var e=this.config.network.ssids.indexOf(t);this.config.network.ssids.splice(e,1),this.update_ssids()},add_ssid:function(){this.config.network.ssids.push(this.ssid),this.ssid="",this.update_ssids()},update_ssids:function(){o.a.get("/api/config/edit/network/ssids/"+this.config.network.ssids.join("|"),{timeout:1e4,headers:{"X-Token":this.jwt}}).then(t=>{t.data.status&&(this.toaster={show:!0,message:"Configuration updated",type:"success"},setTimeout(function(){this.toaster={show:!1}}.bind(this),1e3))}).catch(t=>console.log(t))},change_interface:function(t,e){o.a.get(`/api/config/edit/network/${t}/${e}`,{timeout:1e4,headers:{"X-Token":this.jwt}}).then(s=>{this.toaster={show:!0,message:"Configuration updated",type:"success"},setTimeout(function(){this.toaster={show:!1}}.bind(this),1e3),s.data.status&&(this.config.network[t]=e)}).catch(t=>console.log(t))}},created:function(){this.get_jwt().then(()=>{this.load_config()})}},r=c,l=s("2877"),u=Object(l["a"])(r,i,n,!1,null,null,null);e["default"]=u.exports}}]);
|
||||
//# sourceMappingURL=chunk-3c0d118e.e3f2944c.js.map
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@felixaime/spyguard-backend",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --copy --port=4201",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.6.5",
|
||||
"vue": "^2.6.11",
|
||||
"vue-router": "^3.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||
"@vue/cli-service": "~4.5.0",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="backend-container off-canvas off-canvas-sidebar-show">
|
||||
<div class="backend-navbar">
|
||||
<a class="off-canvas-toggle btn btn-link btn-action" href="#sidebar">
|
||||
<i class="icon icon-menu"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="backend-sidebar off-canvas-sidebar" id="sidebar">
|
||||
<div class="backend-brand">
|
||||
<h2 @click="goto_frontend()" class="title">{{ title }}</h2>
|
||||
<span class="version" v-if="current_version">{{current_version}}</span>
|
||||
</div>
|
||||
<div class="backend-nav">
|
||||
<div class="accordion-container">
|
||||
<div class="accordion">
|
||||
<input id="accordion-configuration" type="checkbox" name="backend-accordion-checkbox" hidden="">
|
||||
<label class="accordion-header c-hand" for="accordion-configuration">Manage Device</label>
|
||||
<div class="accordion-body">
|
||||
<ul class="menu menu-nav">
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/device/configuration')">Device config</span>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/engine/configuration')">Analysis engine</span>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/device/network')">Network config</span>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/device/db')">Manage database</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion">
|
||||
<input id="accordion-iocs" type="checkbox" name="backend-accordion-checkbox" hidden="">
|
||||
<label class="accordion-header c-hand" for="accordion-iocs">Manage IOCs</label>
|
||||
<div class="accordion-body">
|
||||
<ul class="menu menu-nav">
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/iocs/manage')">Manage IOCs</span>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/iocs/search')">Search IOCs</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion">
|
||||
<input id="accordion-whitelist" type="checkbox" name="backend-accordion-checkbox" hidden=""/>
|
||||
<label class="accordion-header c-hand" for="accordion-whitelist">Manage Whitelist</label>
|
||||
<div class="accordion-body">
|
||||
<ul class="menu menu-nav">
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/whitelist/manage')">Manage elements</span>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/whitelist/search')">Search elements</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion">
|
||||
<input id="accordion-instances" type="checkbox" name="backend-accordion-checkbox" hidden=""/>
|
||||
<label class="accordion-header c-hand" for="accordion-instances">External sources</label>
|
||||
<div class="accordion-body">
|
||||
<ul class="menu menu-nav">
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/instances/watchers')">Watchers Instances</span>
|
||||
</li>
|
||||
<li class="menu-item">
|
||||
<span @click="$router.push('/instances/misp')">MISP Instances</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="off-canvas-overlay" href="#close"></a>
|
||||
<div class="off-canvas-content">
|
||||
<div id="update-banner" v-if="update_available" @click="$router.push('/update')">A new version is available, click on the banner to install it.</div>
|
||||
<transition name="fade" mode="out-in">
|
||||
<router-view/>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@import './assets/spectre.min.css';
|
||||
@import './assets/spectre-exp.min.css';
|
||||
@import './assets/spectre-icons.min.css';
|
||||
@import './assets/custom.css';
|
||||
/* Face style for router stuff. */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition-duration: 0.3s;
|
||||
transition-property: opacity;
|
||||
transition-timing-function: ease;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
document.title = 'SpyGuard Backend'
|
||||
|
||||
export default {
|
||||
name: 'backend',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
title: "SPYGUARD",
|
||||
current_version: false,
|
||||
jwt: "",
|
||||
update_available: false,
|
||||
letters: ["SSS§ṠSSSSS","PPPþ⒫PPPP","YYYÿYYYÿYȲYY","GGḠGGGǤG¬G","UÚUUÜUɄUUU", "AAAAÄA¬AAA", "RЯRɌRRRɌʭR", "DD¬DDDDƋDD"]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
generate_random: function(min = 0, max = 1000) {
|
||||
let difference = max - min;
|
||||
let rand = Math.random();
|
||||
rand = Math.floor( rand * difference);
|
||||
rand = rand + min;
|
||||
return rand;
|
||||
},
|
||||
goto_frontend: function() {
|
||||
window.location.href= `http://${location.hostname}:8000`
|
||||
},
|
||||
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)))
|
||||
},
|
||||
get_version: function() {
|
||||
axios.get('/api/update/get-version', { timeout: 60000, headers: { 'X-Token': this.jwt } })
|
||||
.then(response => {
|
||||
if(response.data.status) this.current_version = response.data.current_version
|
||||
})
|
||||
.catch(error => { console.log(error) });
|
||||
},
|
||||
check_update: function() {
|
||||
axios.get('/api/update/check', { timeout: 60000, headers: { 'X-Token': this.jwt } })
|
||||
.then(response => {
|
||||
if(response.data.message == "A new version is available"){
|
||||
this.update_available = true;
|
||||
}
|
||||
})
|
||||
.catch(error => { console.log(error) });
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.get_jwt().then(() => {
|
||||
this.get_version();
|
||||
this.check_update();
|
||||
});
|
||||
setInterval(function(){
|
||||
let res = ""
|
||||
this.letters.forEach(l => { res += l.charAt(this.generate_random(0, 9)) })
|
||||
this.title = res;
|
||||
setTimeout(function(){
|
||||
this.title = "SPYGUARD";
|
||||
}.bind(this), this.generate_random(30, 100));
|
||||
}.bind(this), this.generate_random(500, 10000));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,805 @@
|
||||
/*
|
||||
This CSS was forked from the awsome Spectre.css docs.
|
||||
Spectre.css Docs | MIT License | github.com/picturepan2/spectre
|
||||
*/
|
||||
|
||||
|
||||
.off-canvas .off-canvas-toggle {
|
||||
font-size: 1rem;
|
||||
left: 1.5rem;
|
||||
position: fixed;
|
||||
top: 1rem
|
||||
}
|
||||
|
||||
.off-canvas .off-canvas-sidebar {
|
||||
width: 12rem
|
||||
}
|
||||
|
||||
.off-canvas .off-canvas-content {
|
||||
padding: 0
|
||||
}
|
||||
|
||||
.backend-container {
|
||||
min-height: 100vh
|
||||
}
|
||||
|
||||
.backend-navbar {
|
||||
height: 3.8rem;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 100
|
||||
}
|
||||
|
||||
.backend-navbar .btns {
|
||||
position: absolute;
|
||||
right: 1.5rem;
|
||||
top: 1rem;
|
||||
width: 14rem
|
||||
}
|
||||
|
||||
.backend-navbar .algolia-autocomplete {
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto
|
||||
}
|
||||
|
||||
.backend-sidebar .backend-nav {
|
||||
bottom: 1.5rem;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
overflow-y: auto;
|
||||
padding: .5rem 1.5rem;
|
||||
position: fixed;
|
||||
top: 3.5rem;
|
||||
width: 12rem
|
||||
}
|
||||
|
||||
.backend-sidebar .accordion {
|
||||
margin-bottom: .75rem
|
||||
}
|
||||
|
||||
.backend-sidebar .accordion input~.accordion-header {
|
||||
color: #d9d9d9;
|
||||
font-size: .65rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase
|
||||
}
|
||||
|
||||
.backend-sidebar .accordion input:checked~.accordion-header {
|
||||
color: #d9d9d9;
|
||||
}
|
||||
|
||||
.backend-sidebar .accordion .menu .menu-item {
|
||||
font-size: .7rem;
|
||||
padding-left: 1rem;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.backend-sidebar .accordion .menu .menu-item>span {
|
||||
background: 0 0;
|
||||
color: #FAFAFA;
|
||||
display: inline-block
|
||||
}
|
||||
|
||||
.backend-content {
|
||||
-ms-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
padding: 0 4rem;
|
||||
width: calc(100vw - 12rem);
|
||||
margin-bottom: 10vh;
|
||||
}
|
||||
|
||||
.backend-content>.container {
|
||||
margin-left: 0;
|
||||
max-width: 800px;
|
||||
padding-bottom: 1.5rem
|
||||
}
|
||||
|
||||
.backend-content .anchor {
|
||||
color: #6362dc;
|
||||
display: none;
|
||||
margin-left: .2rem;
|
||||
padding: 0 .2rem
|
||||
}
|
||||
|
||||
.backend-content .anchor:focus,
|
||||
.backend-content .anchor:hover {
|
||||
display: inline;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
.backend-content .s-subtitle,
|
||||
.backend-content .s-title {
|
||||
line-height: 1.8rem;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 1rem;
|
||||
padding-top: 1rem;
|
||||
position: static
|
||||
}
|
||||
|
||||
@supports ((position:-webkit-sticky) or (position:sticky)) {
|
||||
.backend-content .s-subtitle,
|
||||
.backend-content .s-title {
|
||||
position: -webkit-sticky;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 99
|
||||
}
|
||||
.backend-content .s-subtitle::before,
|
||||
.backend-content .s-title::before {
|
||||
background: #fff;
|
||||
bottom: 0;
|
||||
content: "";
|
||||
display: block;
|
||||
left: -10px;
|
||||
position: absolute;
|
||||
right: -10px;
|
||||
top: -5px;
|
||||
z-index: -1
|
||||
}
|
||||
}
|
||||
|
||||
.backend-content .s-subtitle:hover .anchor,
|
||||
.backend-content .s-title:hover .anchor {
|
||||
display: inline
|
||||
}
|
||||
|
||||
.backend-content .s-subtitle+.backend-note,
|
||||
.backend-content .s-title+.backend-note {
|
||||
margin-top: .4rem
|
||||
}
|
||||
|
||||
.backend-content .backend-demo {
|
||||
padding-bottom: 1rem;
|
||||
padding-top: 1rem
|
||||
}
|
||||
|
||||
.backend-content .backend-demo .card {
|
||||
border: 0;
|
||||
box-shadow: 0 .25rem 1rem rgba(48, 55, 66, .15);
|
||||
height: 100%
|
||||
}
|
||||
|
||||
.backend-content .column {
|
||||
padding: .4rem
|
||||
}
|
||||
|
||||
.backend-content .backend-block {
|
||||
border-radius: .1rem;
|
||||
padding: .4rem
|
||||
}
|
||||
|
||||
.backend-content .backend-block.bg-gray {
|
||||
background: #eef0f3
|
||||
}
|
||||
|
||||
.backend-content .backend-shape {
|
||||
height: 4.8rem;
|
||||
line-height: 1.2rem;
|
||||
padding: 1.8rem 0;
|
||||
width: 4.8rem
|
||||
}
|
||||
|
||||
.backend-content .backend-dot {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: .5rem;
|
||||
padding: 0;
|
||||
width: .5rem
|
||||
}
|
||||
|
||||
.backend-content .backend-table td,
|
||||
.backend-content .backend-table th {
|
||||
padding: .75rem .25rem
|
||||
}
|
||||
|
||||
.backend-content .backend-color {
|
||||
border-radius: .1rem;
|
||||
margin: .25rem 0;
|
||||
padding: 5rem .5rem .5rem
|
||||
}
|
||||
|
||||
.backend-content .backend-color .color-subtitle {
|
||||
font-size: .7rem;
|
||||
opacity: .75
|
||||
}
|
||||
|
||||
.backend-content .code .hljs-tag {
|
||||
color: #505c6e
|
||||
}
|
||||
|
||||
.backend-content .code .hljs-comment {
|
||||
color: #bcc3ce
|
||||
}
|
||||
|
||||
.backend-content .code .hljs-class,
|
||||
.backend-content .code .hljs-number,
|
||||
.backend-content .code .hljs-string,
|
||||
.backend-content .code .hljs-title {
|
||||
color: #5755d9
|
||||
}
|
||||
|
||||
.backend-content .code .hljs-attribute,
|
||||
.backend-content .code .hljs-built_in,
|
||||
.backend-content .code .hljs-keyword,
|
||||
.backend-content .code .hljs-name,
|
||||
.backend-content .code .hljs-variable {
|
||||
color: #d73e48
|
||||
}
|
||||
|
||||
.backend-content .code .hljs-hexcolor,
|
||||
.backend-content .code .hljs-value {
|
||||
color: #505c6e
|
||||
}
|
||||
|
||||
.backend-content .c-select-all {
|
||||
-webkit-user-select: all;
|
||||
-moz-user-select: all;
|
||||
-ms-user-select: all;
|
||||
user-select: all
|
||||
}
|
||||
|
||||
.backend-content .panel {
|
||||
height: 75vh
|
||||
}
|
||||
|
||||
.backend-content .panel .tile {
|
||||
margin: .75rem 0
|
||||
}
|
||||
|
||||
.backend-content .parallax {
|
||||
margin: 2rem auto
|
||||
}
|
||||
|
||||
.backend-content .form-autocomplete .menu {
|
||||
position: static
|
||||
}
|
||||
|
||||
.backend-content .example-tile-icon {
|
||||
align-content: space-around;
|
||||
align-items: center;
|
||||
background: #5755d9;
|
||||
border-radius: .1rem;
|
||||
color: #fff;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-align: center;
|
||||
-ms-flex-line-pack: distribute;
|
||||
font-size: 1.2rem;
|
||||
height: 2rem;
|
||||
width: 2rem
|
||||
}
|
||||
|
||||
.backend-content .example-tile-icon .icon {
|
||||
margin: auto
|
||||
}
|
||||
|
||||
.backend-content .comparison-slider {
|
||||
height: auto;
|
||||
padding-bottom: 56.2222%
|
||||
}
|
||||
|
||||
.backend-content .comparison-slider .filter-grayscale {
|
||||
filter: grayscale(75%)
|
||||
}
|
||||
|
||||
.backend-content .off-canvas {
|
||||
position: relative
|
||||
}
|
||||
|
||||
.backend-content .off-canvas .off-canvas-toggle {
|
||||
left: .4rem;
|
||||
position: absolute;
|
||||
top: .4rem;
|
||||
z-index: 1
|
||||
}
|
||||
|
||||
.backend-brand {
|
||||
color: #CCC;
|
||||
height: 2rem;
|
||||
left: 1.5rem;
|
||||
position: fixed;
|
||||
top: .85rem;
|
||||
width: 72%;
|
||||
position: sticky;
|
||||
}
|
||||
|
||||
.backend-brand .backend-logo {
|
||||
align-items: center;
|
||||
border-radius: .1rem;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
-ms-flex-align: center;
|
||||
font-size: .7rem;
|
||||
height: 2rem;
|
||||
padding: .2rem;
|
||||
width: auto
|
||||
}
|
||||
|
||||
.backend-brand .backend-logo:focus,
|
||||
.backend-brand .backend-logo:hover {
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
.backend-brand .backend-logo img {
|
||||
display: inline-block;
|
||||
height: auto;
|
||||
width: 1.6rem
|
||||
}
|
||||
|
||||
.backend-brand .backend-logo h2 {
|
||||
display: inline-block;
|
||||
font-size: .8rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.5rem;
|
||||
margin-bottom: 0;
|
||||
margin-left: .5rem;
|
||||
margin-right: .3rem
|
||||
}
|
||||
|
||||
.backend-footer {
|
||||
color: #bcc3ce;
|
||||
padding: .5rem
|
||||
}
|
||||
|
||||
.backend-footer a {
|
||||
color: #66758c
|
||||
}
|
||||
|
||||
@media (max-width:960px) {
|
||||
.off-canvas .off-canvas-toggle {
|
||||
z-index: 300
|
||||
}
|
||||
.off-canvas .off-canvas-content {
|
||||
width: 100%
|
||||
}
|
||||
.backend-sidebar .backend-brand {
|
||||
margin: .85rem 1.5rem;
|
||||
padding: 0;
|
||||
position: static
|
||||
}
|
||||
.backend-sidebar .backend-nav {
|
||||
margin-top: 1rem;
|
||||
position: static
|
||||
}
|
||||
.backend-sidebar .menu .menu-item>a {
|
||||
padding: .3rem .4rem
|
||||
}
|
||||
.backend-navbar {
|
||||
-webkit-backdrop-filter: blur(5px);
|
||||
backdrop-filter: blur(5px);
|
||||
background: rgba(247, 248, 249, .65);
|
||||
left: 0
|
||||
}
|
||||
.backend-content {
|
||||
min-width: auto;
|
||||
padding: 0 1.5rem;
|
||||
width: 100%
|
||||
}
|
||||
.backend-content .s-subtitle,
|
||||
.backend-content .s-title {
|
||||
padding-top: 5rem;
|
||||
position: static
|
||||
}
|
||||
.backend-content .s-subtitle::before,
|
||||
.backend-content .s-title::before {
|
||||
content: none
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:600px) {
|
||||
.off-canvas .off-canvas-toggle {
|
||||
left: .5rem
|
||||
}
|
||||
.backend-navbar .btns {
|
||||
right: .9rem
|
||||
}
|
||||
.backend-sidebar .backend-brand {
|
||||
margin: .85rem 1rem
|
||||
}
|
||||
.backend-sidebar .backend-nav {
|
||||
padding: .5rem 1rem
|
||||
}
|
||||
.backend-content {
|
||||
padding: 0 .5rem
|
||||
}
|
||||
.backend-content .backend-block {
|
||||
padding: .4rem .1rem
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Lobster';
|
||||
font-weight: normal;
|
||||
src: url('fonts/Exo.ttf') format('truetype');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Roboto-Bold";
|
||||
src: url("fonts/Roboto-Bold.eot"); /* IE9 Compat Modes */
|
||||
src: url("fonts/Roboto-Bold.eot?#iefix") format("embedded-opentype"), /* IE6-IE8 */
|
||||
url("fonts/Roboto-Bold.otf") format("opentype"), /* Open Type Font */
|
||||
url("fonts/Roboto-Bold.svg") format("svg"), /* Legacy iOS */
|
||||
url("fonts/Roboto-Bold.ttf") format("truetype"), /* Safari, Android, iOS */
|
||||
url("fonts/Roboto-Bold.woff") format("woff"), /* Modern Browsers */
|
||||
url("fonts/Roboto-Bold.woff2") format("woff2"); /* Modern Browsers */
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.off-canvas .off-canvas-sidebar {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
font-family: Lobster;
|
||||
color: #484848;
|
||||
}
|
||||
|
||||
h4, h5 {
|
||||
font-family: "Roboto-Bold";
|
||||
color: #484848;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.btn:focus, .btn:hover {
|
||||
background: #494949;
|
||||
border-color: #494949;
|
||||
text-decoration: none;
|
||||
color: #DBDBDB;
|
||||
}
|
||||
|
||||
.btn.active, .btn:active {
|
||||
background: #3a3a3a;
|
||||
border-color: #3a3a3a;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.px150 {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.width-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tab-block {
|
||||
margin-bottom: 35px;
|
||||
}
|
||||
|
||||
.toast {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.frame-export {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-upload {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-upload .upload-field {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
opacity: 0;
|
||||
z-index:1000;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.tlp-white {
|
||||
font-size: .6rem;
|
||||
height: 1.4rem;
|
||||
padding: .05rem .3rem;
|
||||
background-color: #FFF;
|
||||
line-height: 1.2rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
border: 2px solid #efefef;
|
||||
vertical-align: middle;
|
||||
border-radius: .1rem;
|
||||
}
|
||||
|
||||
.tlp-green {
|
||||
font-size: .6rem;
|
||||
height: 1.4rem;
|
||||
padding: .05rem .3rem;
|
||||
background-color: #199a09cf;
|
||||
line-height: 1.2rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
border: 2px solid #0f8600cf;
|
||||
vertical-align: middle;
|
||||
color:#FFF;
|
||||
border-radius: .1rem;
|
||||
}
|
||||
|
||||
.tlp-amber {
|
||||
font-size: .6rem;
|
||||
height: 1.4rem;
|
||||
padding: .05rem .3rem;
|
||||
background-color: #ffc000cf;
|
||||
line-height: 1.2rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
border: 2px solid #ffc000cf;
|
||||
vertical-align: middle;
|
||||
color:#FFF;
|
||||
border-radius: .1rem;
|
||||
}
|
||||
|
||||
.tlp-red {
|
||||
font-size: .6rem;
|
||||
height: 1.4rem;
|
||||
padding: .05rem .3rem;
|
||||
background-color: #ff0033;
|
||||
line-height: 1.2rem;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
border: 2px solid #ff0033;
|
||||
vertical-align: middle;
|
||||
color:#FFF;
|
||||
border-radius: .1rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: 'Lobster';
|
||||
color: #FAFAFA;
|
||||
cursor: pointer;
|
||||
width: fit-content;
|
||||
height: 2rem;
|
||||
left: 1.5rem;
|
||||
position: fixed;
|
||||
top: .85rem;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.3rem;
|
||||
letter-spacing: 0.1rem;
|
||||
}
|
||||
|
||||
#network-thumbnail {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.interfaces-container {
|
||||
margin-top:20px;
|
||||
margin-bottom:40px;
|
||||
}
|
||||
|
||||
.interface-label {
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: inherit;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
border-color: inherit;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.tab .tab-item a:focus {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.shortcuts {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 20000;
|
||||
padding: 20px;
|
||||
opacity: .2;
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.whitespace {
|
||||
height:100px;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.alert-toaster-visible {
|
||||
position:fixed;
|
||||
right:15px;
|
||||
top:15px;
|
||||
padding:10px;
|
||||
background-color: #484848;
|
||||
border-radius: 5px;
|
||||
color: #ccc;
|
||||
font-size: 12px;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: opacity .5s linear;
|
||||
}
|
||||
|
||||
.alert-toaster-hidden {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: visibility 0s .5s, opacity .5s linear;
|
||||
}
|
||||
|
||||
.comment-block {
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
background-color: #FAFAFA;
|
||||
}
|
||||
|
||||
.capi {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.upper {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/*** MISP CSS ***/
|
||||
|
||||
.misp-form {
|
||||
/* Using CSS Grid to lay out the elements in two-dimensions: */
|
||||
display: grid;
|
||||
/* specifying a 0.2em gutter/gap between adjacent elements: */
|
||||
gap: 0.4em;
|
||||
overflow:auto;
|
||||
grid-template-columns: 10em 0.5em 1fr;
|
||||
width: 100%;
|
||||
border-radius:.1rem;
|
||||
margin-bottom: .8rem;
|
||||
}
|
||||
|
||||
.misp-label {
|
||||
/* placing all <label> elements in the grid column 1 (the first): */
|
||||
grid-column: 1;
|
||||
text-align: left;
|
||||
padding-top: .3em;
|
||||
}
|
||||
|
||||
.instance-offline {
|
||||
background-color: #e85600;
|
||||
color: #FFF;
|
||||
font-size: 11px;
|
||||
border-radius: 3px;
|
||||
padding:3px 6px 3px 6px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.instance-online {
|
||||
background-color: #64c800;
|
||||
color: #FFF;
|
||||
font-size: 11px;
|
||||
border-radius: 3px;
|
||||
padding:3px 6px 3px 6px;
|
||||
cursor: help;
|
||||
}
|
||||
.misp-name {
|
||||
font-size: 1rem;
|
||||
font-family: "Roboto-Bold";
|
||||
color: #484848;
|
||||
}
|
||||
|
||||
.misp-name:disabled {
|
||||
border-style: none;
|
||||
color:inherit;
|
||||
background-color: inherit;
|
||||
padding: unset;
|
||||
}
|
||||
|
||||
.misp-input {
|
||||
grid-column: 3;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.misp-input:disabled {
|
||||
border-style: none;
|
||||
color:inherit;
|
||||
background-color: inherit;
|
||||
padding: unset;
|
||||
}
|
||||
|
||||
.misp-button {
|
||||
/* positioning the <button> element in the grid-area identified
|
||||
by the name of 'submit': */
|
||||
grid-area: submit;
|
||||
}
|
||||
|
||||
.loading {
|
||||
margin-bottom: 12px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.loading::after {
|
||||
animation: loading .5s infinite linear;
|
||||
background: 0 0;
|
||||
border: .1rem solid #66758c;
|
||||
border-radius: 50%;
|
||||
border-right-color: transparent;
|
||||
border-top-color: transparent;
|
||||
content: "";
|
||||
display: block;
|
||||
height: .8rem;
|
||||
left: 50%;
|
||||
margin-left: -.4rem;
|
||||
margin-top: -.4rem;
|
||||
opacity: 1;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: .8rem;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.loading.loading-lg::after {
|
||||
height: 1.6rem;
|
||||
margin-left: -.8rem;
|
||||
margin-top: -.8rem;
|
||||
width: 1.6rem;
|
||||
}
|
||||
|
||||
.tooltip::after{
|
||||
background:rgba(48,55,66,.95);
|
||||
border-radius:3px;
|
||||
bottom:100%;
|
||||
color:#fff;
|
||||
content:attr(data-tooltip);
|
||||
display:block;
|
||||
font-size: 11px;
|
||||
left:50%;
|
||||
max-width:320px;
|
||||
opacity:0;
|
||||
overflow:hidden;
|
||||
padding:3px 6px 3px 6px;
|
||||
pointer-events:none;
|
||||
position:absolute;
|
||||
text-overflow:ellipsis;
|
||||
transform:translate(-50%,.4rem);
|
||||
transition:opacity .2s,transform .2s;
|
||||
white-space:pre;
|
||||
z-index:300
|
||||
}
|
||||
|
||||
.btn.btn-primary {
|
||||
background: #3a3a3a;
|
||||
border-color: #3a3a3a;
|
||||
}
|
||||
|
||||
.read-only {
|
||||
background-color: #F4F4F4;
|
||||
color: #AEADAD;
|
||||
}
|
||||
|
||||
#update-banner {
|
||||
padding: 5px 20px;
|
||||
background-color: #d1ff51;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.version {
|
||||
right: 0;
|
||||
text-align: right;
|
||||
background-color: #FFF;
|
||||
border-radius: 5px;
|
||||
font-size: 9px;
|
||||
padding: 1px 3px;
|
||||
color: #000;
|
||||
position: absolute;
|
||||
}
|
||||
|
After Width: | Height: | Size: 805 KiB |
|
After Width: | Height: | Size: 784 KiB |
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,10 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
Vue.config.productionTip = true
|
||||
Vue.config.devtools = true
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
@@ -0,0 +1,87 @@
|
||||
import Vue from 'vue'
|
||||
import VueRouter from 'vue-router'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'default',
|
||||
component: () => import('../views/home.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/device/configuration',
|
||||
name: 'device-configuration',
|
||||
component: () => import('../views/edit-configuration.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/engine/configuration',
|
||||
name: 'engine-configuration',
|
||||
component: () => import('../views/analysis-engine.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/device/network',
|
||||
name: 'device-network',
|
||||
component: () => import('../views/network-manage.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/device/db',
|
||||
name: 'db-manage',
|
||||
component: () => import('../views/db-manage.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/iocs/manage',
|
||||
name: 'iocs-manage',
|
||||
component: () => import('../views/iocs-manage.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/instances/misp',
|
||||
name: 'instance-misp',
|
||||
component: () => import('../views/instance-misp.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/instances/watchers',
|
||||
name: 'instance-watchers',
|
||||
component: () => import('../views/instance-watchers.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/iocs/search',
|
||||
name: 'iocs-search',
|
||||
component: () => import('../views/iocs-search.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/whitelist/manage',
|
||||
name: 'whitelist-manage',
|
||||
component: () => import('../views/whitelist-manage.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/whitelist/search',
|
||||
name: 'whitelist-search',
|
||||
component: () => import('../views/whitelist-search.vue'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/update',
|
||||
name: 'update',
|
||||
component: () => import('../views/update.vue'),
|
||||
props: true
|
||||
}
|
||||
]
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -0,0 +1,143 @@
|
||||
<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="column col-8 col-xs-12">
|
||||
<h3 class="s-title">Detection engine configuration</h3>
|
||||
<h5 class="s-subtitle">Detection methods</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>
|
||||
<label class="form-switch">
|
||||
<input type="checkbox" @change="local_analysis('analysis', 'active')" v-model="config.analysis.active">
|
||||
<i class="form-icon"></i> Use active analysis (Dig, Whois, OpenSSL...).
|
||||
</label>
|
||||
<label class="form-switch">
|
||||
<input type="checkbox" @change="switch_iocs_types('all')" :checked="config.analysis.indicators_types.includes('all')">
|
||||
<i class="form-icon"></i> Detect threats by using all IOCs.
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group" v-if="!config.analysis.indicators_types.includes('all')">
|
||||
<h5 class="s-subtitle">IOCs categories</h5>
|
||||
<label class="form-switch" v-for="tag in iocs_tags" :key="tag">
|
||||
<input type="checkbox" @change="switch_iocs_types(tag)" :checked="config.analysis.indicators_types.includes(tag)">
|
||||
<i class="form-icon"></i> Use IOCs related to {{ tag.toUpperCase() }} threat.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'analysis-engine',
|
||||
data() {
|
||||
return {
|
||||
config: {},
|
||||
check_certificate: false,
|
||||
certificate: "",
|
||||
iocs_tags: [],
|
||||
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)))
|
||||
},
|
||||
local_analysis: function(cat, key) {
|
||||
this.switch_config(cat, key);
|
||||
if (this.config.analysis.remote != false)
|
||||
this.switch_config("analysis", "remote");
|
||||
},
|
||||
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 = ""
|
||||
console.log(this.config.analysis.indicators_types);
|
||||
}
|
||||
})
|
||||
.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)))
|
||||
},
|
||||
get_iocs_tags: function() {
|
||||
axios.get(`/api/ioc/get/tags`, {
|
||||
timeout: 10000,
|
||||
headers: {'X-Token': this.jwt}
|
||||
})
|
||||
.then(response => {
|
||||
if(response.data.tags) this.iocs_tags = response.data.tags
|
||||
})
|
||||
.catch(err => (console.log(err)));
|
||||
},
|
||||
switch_iocs_types: function(tag) {
|
||||
if (this.config.analysis.indicators_types.includes(tag)){
|
||||
axios.get(`/api/config/ioc-type/delete/${tag}`, {
|
||||
timeout: 10000,
|
||||
headers: { 'X-Token': this.jwt }
|
||||
}).then(response => {
|
||||
if (response.data.status) { this.load_config(); }
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
} else {
|
||||
axios.get(`/api/config/ioc-type/add/${tag}`, {
|
||||
timeout: 10000,
|
||||
headers: { 'X-Token': this.jwt }
|
||||
}).then(response => {
|
||||
if (response.data.status) { this.load_config(); }
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
this.load_config();
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.get_jwt().then(() => {
|
||||
this.load_config();
|
||||
this.get_iocs_tags();
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 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 SpyGuard 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>
|
||||
@@ -0,0 +1,167 @@
|
||||
<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="column col-8 col-xs-12">
|
||||
<h3 class="s-title">Configuration </h3>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="user-login">Device UUID (read-only)</label>
|
||||
<div class="input-group">
|
||||
<input class="form-input read-only" id="device-id" v-model="config.device_uuid" readonly="readonly">
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="s-subtitle">Device configuration</h5>
|
||||
<div class="form-group">
|
||||
<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', 'shutdown_option')" v-model="config.frontend.shutdown_option">
|
||||
<i class="form-icon"></i> Allow the end-user to shutdown the device.
|
||||
</label>
|
||||
<label class="form-switch">
|
||||
<input type="checkbox" @change="switch_config('frontend', 'backend_option')" v-model="config.frontend.backend_option">
|
||||
<i class="form-icon"></i> Allow the end-user to access to the backend.
|
||||
</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">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: "",
|
||||
iocs_tags: [],
|
||||
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)))
|
||||
},
|
||||
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>
|
||||
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 col-xs-12">
|
||||
<div class="container">
|
||||
<h3 class="s-title">Getting started</h3>
|
||||
<br />
|
||||
<p>SpyGuard is a forked and enhanced version of TinyCheck, an application developed by Kaspersky. SpyGuard's main objective is to detect signs of compromise by monitoring network flows transmitted by a device.</p>
|
||||
<p>As it uses WiFi, SpyGuard can be used against a wide range of devices, such as smartphones, laptops, IOTs or workstations. To do its job, the analysis engine of SpyGuard is using Indicators of Compromise (IOCs), anomaly detection and is supported by Suricata. </p>
|
||||
|
||||
<p>This backend lets you configure your SpyGuard 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>
|
||||
<p>_</p>
|
||||
</div>
|
||||
<div class="backend-footer container grid-lg" id="copyright">
|
||||
<p>For any question, bug report or feedback, please contact the <a href="mailto:spyguard@protonmail.com" target="_blank">SpyGuard's Team</a> or open an issue on the <a href="https://github.com/SpyGuard/spyguard/issues" target="_blank">SpyGuard Github repository</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,175 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 col-xs-12">
|
||||
<h3 class="s-title">Manage MISP instances</h3>
|
||||
<ul class="tab tab-block">
|
||||
<li class="tab-item">
|
||||
<a href="#" v-on:click="switch_tab('addmisp')" v-bind:class="{ active: tabs.addmisp }">Add instance</a>
|
||||
</li>
|
||||
<li class="tab-item">
|
||||
<a href="#" v-on:click="switch_tab('instances')" v-bind:class="{ active: tabs.instances }">Existing instances</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="tabs.addmisp">
|
||||
<div class="misp-form">
|
||||
<label class="misp-label">Instance name</label><span></span>
|
||||
<input class="form-input" type="text" ref="misp_name" placeholder="CYBERACME MISP" v-model="mispinst.name" required>
|
||||
<label class="misp-label">Instance URL</label><span></span>
|
||||
<input class="form-input" type="text" ref="misp_url" placeholder="https://misp.cyberacme.com" v-model="mispinst.url" required>
|
||||
<label class="misp-label">Authentication key</label><span></span>
|
||||
<input class="form-input" type="text" ref="misp_key" placeholder="OqHSMyAuth3ntic4t10nK3y0MyAuth3ntic4t10nK3y3iiH" v-model="mispinst.key" required>
|
||||
<label class="misp-label" v-if="mispinst.url.startsWith('https://')">Verify certificate? </label><span v-if="mispinst.url.startsWith('https://')"></span>
|
||||
<div style="flex:50%" v-if="mispinst.url.startsWith('https://')"><label class="form-switch">
|
||||
<input type="checkbox" v-model="mispinst.ssl">
|
||||
<i class="form-icon"></i>
|
||||
</label></div>
|
||||
</div>
|
||||
<button class="btn-primary btn col-12" v-on:click="add_instance()">Add MISP instance</button>
|
||||
<div class="form-group" v-if="added">
|
||||
<div class="toast toast-success">
|
||||
✓ MISP instance added successfully. Redirecting to instances in 2 seconds.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="error">
|
||||
<div class="toast toast-error">
|
||||
✗ MISP instance not added. {{error}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="tabs.instances">
|
||||
<div v-if="instances.length">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Server</th>
|
||||
<th>Authkey</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="i in instances" v-bind:key="i.id">
|
||||
<td>{{ i.name }}</td>
|
||||
<td>{{ i.url.replace('https://', '') .replace('http://', '') }}</td>
|
||||
<td>{{ i.apikey.slice(0,5) }} [...] {{ i.apikey.slice(35,40) }}</td>
|
||||
<td>
|
||||
<span v-if="i.connected" class="instance-online tooltip" :data-tooltip="i.lastsync">✓ ONLINE</span>
|
||||
<span v-else class="instance-offline tooltip" :data-tooltip="i.lastsync">⚠ OFFLINE</span>
|
||||
</td>
|
||||
<td><button class="btn btn-sm" v-on:click="delete_instance(i)">Delete</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="empty">
|
||||
<div v-if="loading">
|
||||
<p class="empty-title h5">
|
||||
<span class="loading loading-lg"></span>
|
||||
</p>
|
||||
<p class="empty-subtitle">Testing and loading your MISP instances.</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="empty-title h5">No MISP instance found.</p>
|
||||
<p class="empty-subtitle">Do not hesitate to add a MISP instance.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'managemisp',
|
||||
data() {
|
||||
return {
|
||||
error:false,
|
||||
loading:false,
|
||||
added:false,
|
||||
mispinst:{ name:'', url:'',key:'', ssl:false },
|
||||
instances:[],
|
||||
tabs: { "addmisp" : true, "instances" : false },
|
||||
jwt:""
|
||||
}
|
||||
},
|
||||
props: { },
|
||||
methods: {
|
||||
add_instance: function()
|
||||
{
|
||||
this.added = false;
|
||||
this.error = false;
|
||||
if (this.mispinst.name && this.mispinst.url && this.mispinst.key)
|
||||
{
|
||||
axios.post(`/api/misp/add`, { data: { instance: this.mispinst } }, { headers: {'X-Token': this.jwt} }).then(response => {
|
||||
if(response.data.status){
|
||||
this.added = true;
|
||||
setTimeout(function (){
|
||||
this.switch_tab('instances')
|
||||
this.mispinst = { name:'', url:'',key:'', ssl:false }
|
||||
this.added = false
|
||||
}.bind(this), 2000);
|
||||
} else {
|
||||
this.error = response.data.message;
|
||||
}
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
}
|
||||
},
|
||||
delete_instance(elem)
|
||||
{
|
||||
axios.get(`/api/misp/delete/${elem.id}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||
.then(response => {
|
||||
if(response.data.status){
|
||||
this.instances = this.instances.filter(function(el) { return el != elem; });
|
||||
}
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
},
|
||||
get_misp_instances()
|
||||
{
|
||||
this.loading = true;
|
||||
this.instances = []
|
||||
axios.get(`/api/misp/get_all`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||
.then(response => {
|
||||
if(response.data.results){
|
||||
this.instances = response.data.results;
|
||||
this.instances.forEach(e => {
|
||||
var lastsync = parseInt((Date.now()/1000 - e.lastsync) / 86400)
|
||||
e.lastsync = (!lastsync)? "Synchronized today" : `Synchronized ${lastsync} day(s) ago`
|
||||
} )
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
},
|
||||
switch_tab: function(tab) {
|
||||
|
||||
Object.keys(this.tabs).forEach(key => {
|
||||
if( key == tab ){
|
||||
this.tabs[key] = true
|
||||
if (key == "instances") this.get_misp_instances();
|
||||
} else {
|
||||
this.tabs[key] = false
|
||||
}
|
||||
});
|
||||
},
|
||||
get_jwt(){
|
||||
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>
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 col-xs-12">
|
||||
<h3 class="s-title">Manage watchers instances</h3>
|
||||
<ul class="tab tab-block">
|
||||
<li class="tab-item">
|
||||
<a href="#" v-on:click="switch_tab('addwatcher')" v-bind:class="{ active: tabs.addwatcher }">Add watcher</a>
|
||||
</li>
|
||||
<li class="tab-item">
|
||||
<a href="#" v-on:click="switch_tab('instances')" v-bind:class="{ active: tabs.instances }">Existing watchers</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-if="tabs.addwatcher">
|
||||
<div class="misp-form">
|
||||
<label class="misp-label">Watcher name</label><span></span>
|
||||
<input class="form-input" type="text" ref="watcher_name" placeholder="My incredible watcher" v-model="watcher.name" required>
|
||||
<label class="misp-label">Watcher URL</label><span></span>
|
||||
<input class="form-input" type="text" ref="watcher_url" placeholder="https://url.of.my.watcher.com/watcher.json" v-model="watcher.url" required>
|
||||
<label class="misp-label">Watcher Type</label><span></span>
|
||||
<select class="form-select width-full" placeholder="test" v-model="watcher.type">
|
||||
<option value="iocs">IOCs</option>
|
||||
<option value="whitelist">Whitelist</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn-primary btn col-12" v-on:click="add_instance()">Add watcher</button>
|
||||
<div class="form-group" v-if="added">
|
||||
<div class="toast toast-success">
|
||||
✓ Watcher added successfully. Redirecting to watchers in 2 seconds.
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="error">
|
||||
<div class="toast toast-error">
|
||||
✗ Watcher not added. {{error}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-if="tabs.instances">
|
||||
<div v-if="instances.length">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Type</th>
|
||||
<th>Name</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="i in instances" v-bind:key="i.id">
|
||||
<td>{{ i.type.toUpperCase() }}</td>
|
||||
<td>{{ i.name }}</td>
|
||||
<td>
|
||||
<span v-if="i.status" class="instance-online">✓ ONLINE</span>
|
||||
<span v-else class="instance-offline">⚠ OFFLINE</span>
|
||||
</td>
|
||||
<td><button class="btn btn-sm" v-on:click="delete_instance(i)">Delete</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="empty">
|
||||
<div v-if="loading">
|
||||
<p class="empty-title h5">
|
||||
<span class="loading loading-lg"></span>
|
||||
</p>
|
||||
<p class="empty-subtitle">Testing and loading the watchers.</p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<p class="empty-title h5">No watcher found.</p>
|
||||
<p class="empty-subtitle">Do not hesitate to add a watcher.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'managewatchers',
|
||||
data() {
|
||||
return {
|
||||
error:false,
|
||||
loading:false,
|
||||
added:false,
|
||||
watcher:{ name:'', url:'', type:"iocs" },
|
||||
instances:[],
|
||||
tabs: { "addwatcher" : true, "instances" : false },
|
||||
jwt:""
|
||||
}
|
||||
},
|
||||
props: { },
|
||||
methods: {
|
||||
add_instance: function()
|
||||
{
|
||||
this.added = false;
|
||||
this.error = false;
|
||||
if (this.watcher.name && this.watcher.url && this.watcher.type)
|
||||
{
|
||||
axios.post(`/api/watchers/add`, { data: { instance: this.watcher } }, { headers: {'X-Token': this.jwt} }).then(response => {
|
||||
if(response.data.status){
|
||||
this.added = true;
|
||||
setTimeout(function (){
|
||||
this.switch_tab('instances')
|
||||
this.watcher = { name:'', url:'' }
|
||||
this.added = false
|
||||
}.bind(this), 2000);
|
||||
} else {
|
||||
this.error = response.data.message;
|
||||
}
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
}
|
||||
},
|
||||
delete_instance(elem)
|
||||
{
|
||||
axios.get(`/api/watchers/delete/${elem.id}`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||
.then(response => {
|
||||
if(response.data.status){
|
||||
this.instances = this.instances.filter(function(el) { return el != elem; });
|
||||
}
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
},
|
||||
get_watchers_instances()
|
||||
{
|
||||
this.loading = true;
|
||||
this.instances = []
|
||||
axios.get(`/api/watchers/get_all`, { timeout: 10000, headers: {'X-Token': this.jwt} })
|
||||
.then(response => {
|
||||
if(response.data.results){
|
||||
this.instances = response.data.results;
|
||||
}
|
||||
this.loading = false
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
},
|
||||
switch_tab: function(tab) {
|
||||
Object.keys(this.tabs).forEach(key => {
|
||||
if( key == tab ){
|
||||
this.tabs[key] = true
|
||||
if (key == "instances") this.get_watchers_instances();
|
||||
} else {
|
||||
this.tabs[key] = false
|
||||
}
|
||||
});
|
||||
},
|
||||
get_jwt(){
|
||||
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>
|
||||
@@ -0,0 +1,254 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 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 SpyGuard 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" v-model="tag">
|
||||
<option value="">IOC(s) Tag</option>
|
||||
<option v-for="t in tags" :value="t" :key="t">
|
||||
{{ t.toUpperCase() }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column col-4 col-xs-4">
|
||||
<div class="form-group">
|
||||
<select class="form-select width-full" 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" 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>
|
||||
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-12 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>Tag</th>
|
||||
<th>TLP</th>
|
||||
<th>Source</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="r in results" :key="r.tlp">
|
||||
<td>{{ r.value }}</td>
|
||||
<td class="upper">{{ r.tag }}</td>
|
||||
<td><label :class="['tlp-' + r.tlp]">{{ r.tlp }}</label></td>
|
||||
<td class="capi">{{ r.source }}</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 v-if="loading">
|
||||
<div class="empty">
|
||||
<p class="empty-title h5">
|
||||
<span class="loading loading-lg"></span>
|
||||
</p>
|
||||
<p class="empty-subtitle">Finding your IOC(s)...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'iocs-search',
|
||||
data() {
|
||||
return {
|
||||
results: [],
|
||||
first_search: true,
|
||||
jwt:"",
|
||||
loading:false
|
||||
}
|
||||
},
|
||||
props: { },
|
||||
methods: {
|
||||
search_iocs: function() {
|
||||
this.results = []
|
||||
this.first_search = false
|
||||
this.loading = true;
|
||||
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);
|
||||
}
|
||||
this.loading = false;
|
||||
})
|
||||
.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>
|
||||
@@ -0,0 +1,131 @@
|
||||
<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="column col-8 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">Wireless AP interface</span>
|
||||
<select class="form-select width-full" v-model="iface_in" @change="change_interface('in', iface_in)">
|
||||
<option v-for="iface in config.ifaces_in" :value="iface" :key="iface">
|
||||
{{ iface.toUpperCase() }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="column col-6">
|
||||
<span class="interface-label">Internet link interface</span>
|
||||
<select class="form-select width-full" v-model="iface_out" @change="change_interface('out', iface_out)">
|
||||
<option v-for="iface in config.ifaces_out" :value="iface" :key="iface">
|
||||
{{ iface.toUpperCase() }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="s-subtitle">Edit SSIDs names</h5>
|
||||
<div class="form-group">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network name</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<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: "",
|
||||
iface_in: "",
|
||||
toaster: { show: false, message : "", type : null }
|
||||
}
|
||||
},
|
||||
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
|
||||
this.iface_in = this.config.network.in
|
||||
this.iface_out = this.config.network.out
|
||||
console.log(this.iface_in);
|
||||
}
|
||||
})
|
||||
.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) {
|
||||
this.toaster = { show : true, message : "Configuration updated", type : "success" }
|
||||
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
|
||||
}
|
||||
})
|
||||
.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 => {
|
||||
this.toaster = { show : true, message : "Configuration updated", type : "success" }
|
||||
setTimeout(function () { this.toaster = { show : false } }.bind(this), 1000)
|
||||
if (response.data.status) this.config.network[type] = iface
|
||||
})
|
||||
.catch(err => (console.log(err)))
|
||||
},
|
||||
},
|
||||
created: function() {
|
||||
this.get_jwt().then(() => {
|
||||
this.load_config();
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 col-xs-12">
|
||||
<br />
|
||||
<p><strong>A new SpyGuard version is available ({{next_version}}).</strong><br />
|
||||
<span v-if="!update_launched">Please click on the button below to update your instance.</span>
|
||||
<span v-if="update_launched&&!update_finished">Updating SpyGuard, please wait. You'll be redirected once updated.</span>
|
||||
<span v-if="update_launched&&update_finished" class="color-green">✓ Update finished!</span>
|
||||
</p>
|
||||
<button class="btn btn-primary" :class="[ update_launched ? 'loading' : '' ]" v-on:click="launch_update()" v-if="!update_finished">Update Spyguard</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
|
||||
export default {
|
||||
name: 'update',
|
||||
data() {
|
||||
return {
|
||||
translation: {},
|
||||
update_launched: null,
|
||||
check_interval: null,
|
||||
next_version: null,
|
||||
current_version: null,
|
||||
update_finished: false,
|
||||
jwt: "",
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
check_version: function() {
|
||||
axios.get('/api/update/get-version', { timeout: 60000, headers: { 'X-Token': this.jwt } })
|
||||
.then(response => {
|
||||
if(response.data.status) {
|
||||
if(response.data.current_version == this.next_version){
|
||||
window.current_version = response.data.current_version
|
||||
this.update_finished = true
|
||||
clearInterval(this.check_interval);
|
||||
setTimeout(function () { window.location.href = "/"; }, 3000)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => { console.log(error) });
|
||||
},
|
||||
launch_update: function() {
|
||||
axios.get(`/api/update/process`, { timeout: 60000, headers: { 'X-Token': this.jwt } })
|
||||
.then(response => {
|
||||
if(response.data.status) {
|
||||
if(response.data.message == "Update successfully launched"){
|
||||
this.update_launched = true
|
||||
this.check_interval = setInterval(function(){ this.check_version(); }.bind(this), 3000);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => { console.log(error) });
|
||||
},
|
||||
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)))
|
||||
},
|
||||
get_versions: function() {
|
||||
axios.get('/api/update/check', { timeout: 60000, headers: { 'X-Token': this.jwt } })
|
||||
.then(response => {
|
||||
if(response.data.status){
|
||||
this.current_version = response.data.current_version
|
||||
this.next_version = response.data.next_version
|
||||
if(this.current_version == this.next_version) window.location.href = "/";
|
||||
}
|
||||
})
|
||||
.catch(error => { console.log(error) });
|
||||
},
|
||||
},
|
||||
created: function() {
|
||||
this.get_jwt().then(() => {
|
||||
this.get_versions();
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-8 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 SpyGuard 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>
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="backend-content" id="content">
|
||||
<div class="column col-12 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>
|
||||
@@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
devServer: {
|
||||
proxy: {
|
||||
'^/api': {
|
||||
target: 'https://localhost:5000',
|
||||
ws: true,
|
||||
changeOrigin: true
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
timeout=60000
|
||||
@@ -0,0 +1,24 @@
|
||||
# tinycheck-new
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"python.pythonPath": "/usr/local/opt/python@3.8/bin/python3.8"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.canvas-anim{height:120px;margin:0 auto;position:relative;width:205px}.canvas-anim.anim-connect{width:300px}.canvas-anim.anim-connect .icon-usb{animation:slide-right 1s cubic-bezier(.455,.03,.515,.955) infinite alternate both}
|
||||
@@ -0,0 +1 @@
|
||||
#app{overflow-y:visible}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*!
|
||||
*
|
||||
* simple-keyboard v2.32.131
|
||||
* https://github.com/hodgef/simple-keyboard
|
||||
*
|
||||
* Copyright (c) Francisco Hodge (https://github.com/hodgef)
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
*/.hg-theme-default{width:100%;-webkit-user-select:none;user-select:none;box-sizing:border-box;overflow:hidden;touch-action:manipulation}.hg-theme-default .hg-button span{pointer-events:none}.hg-theme-default button.hg-button{border-width:0;outline:0;font-size:inherit}.hg-theme-default{font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;background-color:#ececec;padding:5px;border-radius:5px}.hg-theme-default .hg-button{display:inline-block;flex-grow:1}.hg-theme-default .hg-row{display:flex}.hg-theme-default .hg-row:not(:last-child){margin-bottom:5px}.hg-theme-default .hg-row .hg-button-container,.hg-theme-default .hg-row .hg-button:not(:last-child){margin-right:5px}.hg-theme-default .hg-row>div:last-child{margin-right:0}.hg-theme-default .hg-row .hg-button-container{display:flex}.hg-theme-default .hg-button{box-shadow:0 0 3px -1px rgba(0,0,0,.3);height:40px;border-radius:5px;box-sizing:border-box;padding:5px;background:#fff;border-bottom:1px solid #b5b5b5;cursor:pointer;display:flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:rgba(0,0,0,0)}.hg-theme-default .hg-button.hg-activeButton{background:#efefef}.hg-theme-default.hg-layout-numeric .hg-button{width:33.3%;height:60px;align-items:center;display:flex;justify-content:center}.hg-theme-default .hg-button.hg-button-numpadadd,.hg-theme-default .hg-button.hg-button-numpadenter{height:85px}.hg-theme-default .hg-button.hg-button-numpad0{width:105px}.hg-theme-default .hg-button.hg-button-com{max-width:85px}.hg-theme-default .hg-button.hg-standardBtn.hg-button-at{max-width:45px}.hg-theme-default .hg-button.hg-selectedButton{background:rgba(5,25,70,.53);color:#fff}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=".com"]{max-width:82px}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"]{max-width:60px}
|
||||
|
After Width: | Height: | Size: 784 KiB |
@@ -0,0 +1,16 @@
|
||||
<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="40px" height="40px" viewBox="0 0 40 40" enable-background="new 0 0 40 40" xml:space="preserve">
|
||||
<path opacity="0.2" fill="#000" d="M20.201,5.169c-8.254,0-14.946,6.692-14.946,14.946c0,8.255,6.692,14.946,14.946,14.946
|
||||
s14.946-6.691,14.946-14.946C35.146,11.861,28.455,5.169,20.201,5.169z M20.201,31.749c-6.425,0-11.634-5.208-11.634-11.634
|
||||
c0-6.425,5.209-11.634,11.634-11.634c6.425,0,11.633,5.209,11.633,11.634C31.834,26.541,26.626,31.749,20.201,31.749z"/>
|
||||
<path fill="#f7f8f9" d="M26.013,10.047l1.654-2.866c-2.198-1.272-4.743-2.012-7.466-2.012h0v3.312h0
|
||||
C22.32,8.481,24.301,9.057,26.013,10.047z">
|
||||
<animateTransform attributeType="xml"
|
||||
attributeName="transform"
|
||||
type="rotate"
|
||||
from="0 20 20"
|
||||
to="360 20 20"
|
||||
dur="0.5s"
|
||||
repeatCount="indefinite"/>
|
||||
</path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 970 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="106" height="106" viewBox="0 0 106 106" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="53" cy="53" r="53" fill="#40D8A1"/>
|
||||
<path d="M29 52.5L47.5 70.5" stroke="white" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<line x1="79" y1="40.0711" x2="48.0711" y2="71" stroke="white" stroke-width="10" stroke-linecap="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 377 B |
@@ -0,0 +1,5 @@
|
||||
<svg width="568" height="179" viewBox="0 0 568 179" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M568 15H425V166H568V15ZM505 42H527V67H505V42ZM505 113H527V138H505V113Z" fill="#D9D9D9"/>
|
||||
<path d="M25 26C25 42.125 6.1371 63.9636 6.00087 89.8798C5.86219 116.263 25 141.356 25 155" stroke="#3A3A3A" stroke-width="10"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M50 0C33.4315 0 20 12.0816 20 26.9849V152.015C20 166.919 33.4315 179 50 179H448C452.418 179 456 175.778 456 171.804V7.19598C456 3.22175 452.418 0 448 0H50ZM260.449 77.4359L284 91.0042L260.449 104.571V94.8762H169.513L192.956 119.585C194.95 121.18 197.563 122.298 200.248 122.359C214.287 122.359 219.225 122.368 219.225 122.368V114.501H242.782V138H219.225V130.122L200.477 130.126C195.204 130.126 189.677 127.242 186.161 123.86C186.259 123.958 186.358 124.068 186.159 123.858C186.082 123.773 161.295 97.6508 161.295 97.6508C159.307 96.0562 156.702 94.9429 154.024 94.8748H124.433C124.398 94.8767 124.364 94.8748 124.329 94.8748H110.951C109.146 103.857 101.202 110.624 91.6709 110.624C80.8066 110.624 72 101.838 72 90.9991C72 80.1635 80.8066 71.3776 91.6709 71.3776C101.2 71.3776 109.144 78.1393 110.949 87.1139H124.566C127.249 87.0501 129.857 85.9382 131.848 84.3454C131.848 84.3454 156.64 58.2211 156.715 58.1375C156.916 57.928 156.816 58.0351 156.72 58.1356C160.237 54.7524 165.761 51.8665 171.034 51.8665L190.487 51.8698C192.096 47.2996 196.419 44 201.558 44C208.059 44 213.334 49.2609 213.334 55.7516C213.334 62.2395 208.058 67.5023 201.558 67.5023C196.419 67.5023 192.096 64.2009 190.487 59.6269C188.113 59.6316 181.664 59.6344 170.803 59.6344C168.118 59.6964 165.509 60.8154 163.514 62.4105L140.076 87.1149H260.449V77.4359Z" fill="#3A3A3A"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |