First commit

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

View File

@ -0,0 +1,60 @@
<template>
<div :class="keyboardClass"></div>
</template>
<script>
import Keyboard from "simple-keyboard";
import "simple-keyboard/build/css/index.css";
export default {
name: "SimpleKeyboard",
props: {
keyboardClass: {
default: "simple-keyboard",
type: String
},
input: {
type: String
}
},
data: () => ({
keyboard: null
}),
mounted() {
this.keyboard = new Keyboard({
onChange: this.onChange,
onKeyPress: this.onKeyPress
});
},
methods: {
onChange(input) {
this.$emit("onChange", input);
},
onKeyPress(button) {
this.$emit("onKeyPress", button);
/**
* If you want to handle the shift and caps lock buttons
*/
if (button === "{shift}" || button === "{lock}") this.handleShift();
},
handleShift() {
let currentLayout = this.keyboard.options.layoutName;
let shiftToggle = currentLayout === "default" ? "shift" : "default";
this.keyboard.setOptions({
layoutName: shiftToggle
});
}
},
watch: {
input(input) {
this.keyboard.setInput(input);
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

View File

@ -0,0 +1,70 @@
<template>
<div class="center">
<div v-if="question">
<p>Do you want to analyze the captured communications?</p>
<div class="empty-action">
<button class="btn" v-on:click="save_capture()">No, just save them</button> <button class="btn btn-primary" v-on:click="start_analysis()">Yes, let's do it</button>
</div>
</div>
<div v-else-if="running">
<img src="@/assets/loading.svg"/>
<p class="legend" v-if="!long_waiting">Please wait during the analysis...</p>
<p class="legend fade-in" v-if="long_waiting">Yes, it can take some time...</p>
</div>
</div>
</template>
<script>
import router from '../router'
import axios from 'axios'
export default {
name: 'analysis',
data() {
return {
question: true,
running: false,
check_alerts: false,
long_waiting: false
}
},
props: {
capture_token: String
},
methods: {
start_analysis: function() {
this.question = false
this.running = true
setTimeout(function () { this.long_waiting = true }.bind(this), 15000);
axios.get(`/api/analysis/start/${this.capture_token}`, { timeout: 60000 })
.then(response => {
if(response.data.message == "Analysis started")
this.check_alerts = setInterval(() => { this.get_alerts(); }, 500);
})
.catch(error => {
console.log(error);
});
},
get_alerts: function() {
axios.get(`/api/analysis/report/${this.capture_token}`, { timeout: 60000 })
.then(response => {
if(response.data.message != "No report yet"){
clearInterval(this.check_alerts);
this.long_waiting = false;
this.running = false;
router.replace({ name: 'report', params: { alerts : response.data.alerts,
device : response.data.device,
capture_token : this.capture_token } });
}
})
.catch(error => {
console.log(error);
});
},
save_capture: function() {
var capture_token = this.capture_token
router.replace({ name: 'save-capture', params: { capture_token: capture_token } });
}
}
}
</script>

View File

@ -0,0 +1,100 @@
<template>
<div class="capture-wrapper">
<svg id="sparkline" stroke-width="3" :width="sparkwidth" :height="sparkheight" v-if="sparklines"></svg>
<div class="center">
<div class="footer">
<h3 class="timer">{{timer_hours}}:{{timer_minutes}}:{{timer_seconds}}</h3>
<p>Intercepting the communications of {{device_name}}.</p>
<div class="empty-action">
<button class="btn" :class="[ loading ? 'loading' : 'btn-primary', ]" v-on:click="stop_capture()">Stop the capture</button>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import router from '../router'
import sparkline from '@fnando/sparkline'
export default {
name: 'capture',
components: {},
data() {
return {
timer_hours: "00",
timer_minutes: "00",
timer_seconds: "00",
loading: false,
stats_interval: false,
chrono_interval: false,
sparklines: false
}
},
props: {
capture_token: String,
device_name: String
},
methods: {
set_chrono: function() {
this.chrono_interval = setInterval(() => { this.chrono(); }, 10);
},
stop_capture: function() {
this.loading = true
axios.get(`/api/network/ap/stop`, { timeout: 30000 })
axios.get(`/api/capture/stop`, { timeout: 30000 })
.then(response => (this.handle_finish(response.data)))
},
get_stats: function() {
axios.get(`/api/capture/stats`, { timeout: 30000 })
.then(response => (this.handle_stats(response.data)))
},
handle_stats: function(data) {
if (data.packets.length) sparkline(document.querySelector("#sparkline"), data.packets);
},
handle_finish: function(data) {
clearInterval(this.chrono_interval);
clearInterval(this.stats_interval);
if (data.status) {
this.loading = false
var capture_token = this.capture_token
router.replace({ name: 'analysis', params: { capture_token: capture_token } });
}
},
chrono: function() {
var time = Date.now() - this.capture_start
this.timer_hours = Math.floor(time / (60 * 60 * 1000));
this.timer_hours = (this.timer_hours < 10) ? "0" + this.timer_hours : this.timer_hours
time = time % (60 * 60 * 1000);
this.timer_minutes = Math.floor(time / (60 * 1000));
this.timer_minutes = (this.timer_minutes < 10) ? "0" + this.timer_minutes : this.timer_minutes
time = time % (60 * 1000);
this.timer_seconds = Math.floor(time / 1000);
this.timer_seconds = (this.timer_seconds < 10) ? "0" + this.timer_seconds : this.timer_seconds
},
setup_sparklines: function() {
axios.get(`/api/misc/config`, { timeout: 60000 })
.then(response => {
if(response.data.sparklines){
this.sparklines = true
this.sparkwidth = window.screen.width + "px";
this.sparkheight = Math.trunc(window.screen.height / 5) + "px";
this.stats_interval = setInterval(() => { this.get_stats(); }, 500);
}
})
.catch(error => {
console.log(error)
});
}
},
created: function() {
// Get the config for the sparklines.
this.setup_sparklines()
// Start the chrono and get the first stats.
this.capture_start = Date.now()
this.set_chrono();
}
}
</script>

View File

@ -0,0 +1,119 @@
<template>
<div class="center">
<div v-if="(error == false)">
<div v-if="ssid_name">
<div class="card apcard" v-on:click="generate_ap()">
<div class="columns">
<div class="column col-5">
<center><img :src="ssid_qr" id="qrcode"></center>
</div>
<div class="divider-vert white-bg" data-content="OR"></div>
<div class="column col-5"><br />
<span class="light-grey">Network name: </span><br />
<h4>{{ ssid_name }}</h4>
<span class="light-grey">Network password: </span><br />
<h4>{{ ssid_password }}</h4>
</div>
</div>
</div>
<br /><br /><br /><br /> <br /><br /><br /><br /><br /><br />
<!-- Requite a CSS MEME for that shit :) -->
<span class="legend">Tap the white frame to generate a new network.</span>
</div>
<div v-else>
<img src="@/assets/loading.svg"/>
<p class="legend">We generate an ephemeral network for you.</p>
</div>
</div>
<div v-else>
<p>
<strong>Unfortunately, we got some issues.</strong>
<br /><br />
Please verify that you've two Wifi interfaces on your device<br />
and restart it by clicking on the button below.<br />
</p>
<button class="btn" v-on:click="reboot()">Reboot the device</button>
</div>
</div>
</template>
<script>
import axios from 'axios'
import router from '../router'
export default {
name: 'generate-ap',
components: {},
data() {
return {
ssid_name: false,
ssid_qr: false,
ssid_password: false,
capture_token: false,
capture_start: false,
interval: false,
error: false
}
},
methods: {
generate_ap: function() {
clearInterval(this.interval)
this.ssid_name = false
axios.get(`/api/network/ap/start`, { timeout: 30000 })
.then(response => (this.show_ap(response.data)))
},
show_ap: function(data) {
if (data.status) {
this.ssid_name = data.ssid
this.ssid_password = data.password
this.ssid_qr = data.qrcode
this.start_capture() // Start the capture before client connect.
} else {
this.error = true
}
},
start_capture: function() {
axios.get(`/api/capture/start`, { timeout: 30000 })
.then(response => (this.get_capture_token(response.data)))
},
reboot: function() {
axios.get(`/api/misc/reboot`, { timeout: 30000 })
.then(response => { console.log(response)})
},
get_capture_token: function(data) {
if (data.status) {
this.capture_token = data.capture_token
this.capture_start = Date.now()
this.get_device()
}
},
get_device: function() {
this.interval = setInterval(() => {
axios.get(`/api/device/get/${this.capture_token}`, { timeout: 30000 })
.then(response => (this.check_device(response.data)))
}, 500);
},
check_device: function(data) {
if (data.status) {
clearInterval(this.interval);
var capture_token = this.capture_token
var capture_start = this.capture_start
var device_name = data.name
router.replace({
name: 'capture',
params: {
capture_token: capture_token,
capture_start: capture_start,
device_name: device_name
}
});
}
}
},
created: function() {
this.generate_ap();
}
}
</script>

View File

@ -0,0 +1,24 @@
<template>
<div class="center">
<h3 class="lobster">Welcome to TinyCheck.</h3>
<p>We are going to help you to check your device.</p>
<button class="btn btn-primary" v-on:click="next()">Let's start!</button>
</div>
</template>
<script>
import router from '../router'
export default {
name: 'home',
props: { saved_ssid: String, list_ssids: Array, internet: Boolean },
methods: {
next: function() {
var saved_ssid = this.saved_ssid
var list_ssids = this.list_ssids
var internet = this.internet
router.push({ name: 'wifi-select', params: { saved_ssid: saved_ssid, list_ssids: list_ssids, internet:internet } });
}
}
}
</script>

View File

@ -0,0 +1,115 @@
<template>
<div>
<div v-if="results">
<div v-if="alerts.high.length >= 1" class="high-wrapper">
<div class="center">
<h1 class="warning-title">You have {{ nb_translate(alerts.high.length) }} high alert,<br />your device seems to be compromised.</h1>
<button class="btn btn-report-low-light" v-on:click="new_capture()">Start a new capture</button>
<button class="btn btn-report-high" @click="show_report=true;results=false;">Show the full report</button>
</div>
</div>
<div v-else-if="alerts.moderate.length >= 1" class="med-wrapper">
<div class="center">
<h1 class="warning-title">You have {{ nb_translate(alerts.moderate.length) }} moderate alerts,<br />your device might be compromised.</h1>
<button class="btn btn-report-low-light" v-on:click="new_capture()">Start a new capture</button>
<button class="btn btn-report-moderate" @click="show_report=true;results=false;">Show the full report</button>
</div>
</div>
<div v-else-if="alerts.low.length >= 1" class="low-wrapper">
<div class="center">
<h1 class="warning-title">You have ony {{ nb_translate(alerts.moderate.low) }} low alerts,<br /> don't hesitate to check them.</h1>
<button class="btn btn-report-low-light" v-on:click="new_capture()">Start a new capture</button>
<button class="btn btn-report-low" @click="show_report=true;results=false;">Show the full report</button>
</div>
</div>
<div v-else class="none-wrapper">
<div class="center">
<h1 class="warning-title">Everything looks fine, zero alerts.</h1>
<button class="btn btn-report-low-light" v-on:click="save_capture()">Save the capture</button>
<button class="btn btn-report-low" v-on:click="new_capture()">Start a new capture</button>
</div>
</div>
</div>
<div v-else-if="show_report" class="report-wrapper">
<div class="device-ctx">
<h3 style="margin: 0;">Report for {{device.name}}</h3>
IP Address: {{device.ip_address}}<br />Mac Address: {{device.mac_address}}
</div>
<ul class="alerts">
<li class="alert" v-for="alert in alerts.high" :key="alert.message">
<span class="high-label">High</span><span class="alert-id">{{ alert.id }}</span>
<div class="alert-body">
<span class="title">{{ alert.title }}</span>
<p class="description">{{ alert.description }}</p>
</div>
</li>
<li class="alert" v-for="alert in alerts.moderate" :key="alert.message">
<span class="moderate-label">Moderate</span><span class="alert-id">{{ alert.id }}</span>
<div class="alert-body">
<span class="title">{{ alert.title }}</span>
<p class="description">{{ alert.description }}</p>
</div>
</li>
<li class="alert" v-for="alert in alerts.low" :key="alert.message">
<span class="low-label">Low</span><span class="alert-id">{{ alert.id }}</span>
<div class="alert-body">
<span class="title">{{ alert.title }}</span>
<p class="description">{{ alert.description }}</p>
</div>
</li>
</ul>
<div class="columns" id="controls-analysis">
<div class="column col-5">
<button class="btn width-100" @click="$router.push('generate-ap')">Start a capture</button>
</div>
<div class="divider-vert column col-2" data-content="OR"></div>
<div class="column col-5">
<button class="btn btn btn-primary width-100" v-on:click="save_capture()">Save the report</button>
</div>
</div>
</div>
</div>
</template>
<style>
#app {
overflow-y: visible;
}
</style>
<script>
import router from '../router'
export default {
name: 'report',
data() {
return {
results: true,
}
},
props: {
device: Object,
alerts: Array,
capture_token: String
},
methods: {
save_capture: function() {
var capture_token = this.capture_token
router.replace({ name: 'save-capture', params: { capture_token: capture_token } });
},
new_capture: function() {
router.push({ name: 'generate-ap' })
},
nb_translate: function(x) {
var nbs = ['zero','one','two','three','four', 'five','six','seven','eight','nine', 'ten', 'eleven']
try {
return nbs[x];
} catch (error)
{
return x;
}
}
}
}
</script>

View File

@ -0,0 +1,200 @@
<template>
<div class="center" v-if="save_usb && init">
<div class="canvas-anim" :class="{'anim-connect': !saved && !usb}" v-on:click="new_capture()">
<div class="icon-spinner" v-if="!saved && usb"></div>
<div class="icon-success" v-if="saved"></div>
<div class="icon-usb"></div>
<div class="icon-usb-plug"></div>
</div>
<p class="legend" v-if="!saved && !usb"><br />Please connect a USB key to save your capture.</p>
<p class="legend" v-if="!saved && usb"><br />We are saving your capture.</p>
<p class="legend" v-if="saved"><br />You can tap the USB key to start a new capture.</p>
</div>
<div class="center" v-else-if="!save_usb && init">
<div>
<p class="legend">The capture download is going to start...<br /><br /><br /></p>
<button class="btn btn-primary" v-on:click="new_capture()">Start another capture</button>
<iframe :src="download_url" class="frame-download"></iframe>
</div>
</div>
</template>
<style lang="scss">
.canvas-anim {
height: 120px;
margin: 0 auto;
position: relative;
width: 205px;
&.anim-connect {
width: 300px;
.icon-usb {
-webkit-animation: slide-right 1s cubic-bezier(0.455, 0.030, 0.515, 0.955) infinite alternate both;
animation: slide-right 1s cubic-bezier(0.455, 0.030, 0.515, 0.955) infinite alternate both;
}
}
}
.icon-usb {
background: url('../assets/icon_usb.svg') no-repeat 0 0;
background-size: 200px auto;
display: block;
height: 120px;
position: absolute;
top: 25px;
left: 0;
width: 200px;
z-index: 8;
}
.icon-usb-plug {
background: url('../assets/icon_plug_usb.svg') no-repeat 0 0;
background-size: cover;
display: block;
height: 120px;
position: absolute;
top: 0;
right: -10px;
width: 55px;
z-index: 9;
}
.icon-success {
background: url('../assets/icon_success.svg') no-repeat 0 0;
background-size: 80px auto;
display: block;
position: absolute;
height: 120px;
top: -25px;
left: -40px;
width: 80px;
z-index: 10;
-webkit-animation: scale-down-center 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
animation: scale-down-center 0.7s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
}
.icon-spinner {
background: url('../assets/icon_spinner.svg') no-repeat 0 0;
background-color: #f7f8f9;
border-radius: 40px;
display: block;
height: 40px;
position: absolute;
top: 5px;
left: -20px;
width: 40px;
z-index: 10;
}
@-webkit-keyframes slide-right {
0% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
100% {
-webkit-transform: translateX(75px);
transform: translateX(75px);
}
}
@keyframes slide-right {
0% {
-webkit-transform: translateX(0);
transform: translateX(0);
}
100% {
-webkit-transform: translateX(75px);
transform: translateX(75px);
}
}
@-webkit-keyframes scale-down-center {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
100% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
}
@keyframes scale-down-center {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
100% {
-webkit-transform: scale(0.5);
transform: scale(0.5);
}
}
</style>
<script>
import axios from 'axios'
import router from '../router'
export default {
name: 'save-capture',
components: {},
data() {
return {
usb: false,
saved: false,
save_usb: false,
init: false
}
},
props: {
capture_token: String
},
methods: {
check_usb: function() {
axios.get(`/api/save/usb-check`, { timeout: 30000 })
.then(response => {
if(response.data.status) {
this.usb = true
clearInterval(this.interval)
this.save_capture()
}
})
},
save_capture: function() {
var capture_token = this.capture_token
axios.get(`/api/save/save-capture/${capture_token}/usb`, { timeout: 30000 })
.then(response => {
if(response.data.status){
this.saved = true
this.timeout = setTimeout(() => router.push('/'), 60000);
}
})
},
new_capture: function() {
clearTimeout(this.timeout);
router.push({ name: 'generate-ap' })
},
load_config: function() {
axios.get(`/api/misc/config`, { timeout: 60000 })
.then(response => {
if(response.data.download_links){
this.init = true
this.save_usb = false
this.download_url = `/api/save/save-capture/${this.capture_token}/url`
} else {
this.init = true
this.save_usb = true
this.interval = setInterval(() => { this.check_usb() }, 500);
}
})
.catch(error => {
console.log(error)
});
}
},
created: function() {
this.load_config()
}
}
</script>

View File

@ -0,0 +1,53 @@
<template>
<div class="center">
<img src="@/assets/logo.png" id="tinycheck-logo" />
<div class="loading loading-lg loadingsplash"></div>
</div>
</template>
<script>
import router from '../router'
import axios from 'axios'
export default {
name: 'splash-screen',
components: {},
data() {
return {
list_ssids: [],
internet: false
}
},
methods: {
// Check if the device is already connected to internet.
internet_check: function() {
axios.get(`/api/network/status`, { timeout: 10000 })
.then(response => {
if (response.data.internet) this.internet = true
this.get_wifi_networks()
})
.catch(err => (console.log(err)))
},
// Get the WiFi networks around the box.
get_wifi_networks: function() {
axios.get(`/api/network/wifi/list`, { timeout: 10000 })
.then(response => (this.append_ssids(response.data.networks)))
.catch(err => (console.log(err)))
},
// Handle the get_wifi_networks answer and call goto_home.
append_ssids: function(networks) {
this.list_ssids = networks
this.goto_home()
},
// Pass the list of ssids and the internet status as a prop to the home view.
goto_home: function() {
var list_ssids = this.list_ssids
var internet = this.internet
router.replace({ name: 'home', params: { list_ssids: list_ssids, internet: internet } });
}
},
created: function() {
this.internet_check();
}
}
</script>

View File

@ -0,0 +1,166 @@
<template>
<div :class="[ keyboard == false ? 'center' : '' ]">
<div v-if="keyboard == false">
<div v-if="have_internet">
<p>You seem to be already connected to a network.<br />Do you want to use the current connection?</p>
<div class="empty-action">
<button class="btn" @click="have_internet = false">No, use another</button> <button class="btn" :class="[ connecting ? 'loading' : '', success ? 'btn-success' : 'btn-primary', ]" @click="$router.push({ name: 'generate-ap' })">Yes, use it.</button>
</div>
</div>
<div v-else>
<div v-if="enter_creds" class="wifi-login">
<div class="form-group">
<select class="form-select" id="ssid-select" v-model="ssid">
<option value="" selected>Wifi name</option>
<option v-for="ssid in ssids" v-bind:key="ssid.ssid">
{{ ssid.ssid }}
</option>
</select>
</div>
<div class="form-group">
<input class="form-input" type="password" id="password" v-model="password" placeholder="Wifi password" v-on:click="keyboard = (virtual_keyboard)? true : false">
</div>
<div class="form-group">
<button class="btn width-100" :class="[ connecting ? 'loading' : '', success ? 'btn-success' : 'btn-primary', ]" v-on:click="wifi_setup()">{{ btnval }}</button>
</div>
<div class="form-group">
<button class="btn width-100" :class="[ refreshing ? 'loading' : '' ]" v-on:click="refresh_wifi_list()">Refresh networks list</button>
</div>
</div>
<div v-else>
<p><strong>You seem to not be connected to Internet.</strong><br />Please configure the Wi-Fi connection.</p>
<div class="empty-action">
<button class="btn btn-primary" @click="enter_creds = true">Ok, let's do that.</button>
</div>
</div>
</div>
</div>
<div v-else>
<input :value="input" class="keyboardinput" @input="onInputChange" placeholder="Tap on the virtual keyboard to start">
<SimpleKeyboard @onChange="onChange" @onKeyPress="onKeyPress" :input="input" />
</div>
</div>
</template>
<style>
#app {
overflow-y: hidden;
}
</style>
<script>
import axios from 'axios'
import router from '../router'
import SimpleKeyboard from "./SimpleKeyboard";
export default {
name: 'wifi-select',
components: {
SimpleKeyboard
},
data() {
return {
connecting: false,
error: false,
success: false,
btnval: "Connect to it.",
ssid: "",
selected_ssid: false,
password: "",
keyboard: false,
input: "",
ssids: [],
have_internet: false,
enter_creds: false,
virtual_keyboard: false,
refreshing: false
}
},
props: {
saved_ssid: String,
list_ssids: Array,
internet: Boolean
},
methods: {
wifi_connect: function() {
axios.get(`/api/network/wifi/connect`, { timeout: 60000 })
.then(response => {
if (response.data.status) {
this.success = true
this.connecting = false
this.btnval = "Wifi connected!"
setTimeout(() => router.push('generate-ap'), 1000);
} else {
this.btnval = "Wifi not connected. Please retry."
this.connecting = false
}
})
.catch(error => {
console.log(error)
});
},
wifi_setup: function() {
if (this.ssid.length && this.password.length >= 8 ){
axios.post(`/api/network/wifi/setup`, { ssid: this.ssid, password: this.password }, { timeout: 60000 })
.then(response => {
if(response.data.status) {
this.connecting = true
this.wifi_connect()
} else {
console.log(response.data.message)
}
})
.catch(error => {
console.log(error)
});
}
},
load_config: function() {
axios.get(`/api/misc/config`, { timeout: 60000 })
.then(response => {
this.virtual_keyboard = response.data.virtual_keyboard
})
.catch(error => {
console.log(error)
});
},
onChange(input) {
this.input = input
this.password = this.input;
},
onKeyPress(button) {
if (button == "{enter}")
this.keyboard = false
},
onInputChange(input) {
this.input = input.target.value;
},
append_ssids: function(networks) {
this.ssids = networks
},
refresh_wifi_list: function(){
this.refreshing = true
axios.get(`/api/network/wifi/list`, { timeout: 10000 })
.then(response => {
this.refreshing = false
this.append_ssids(response.data.networks)
}).catch(error => {
this.refreshing = false
console.log(error)
});
}
},
created: function() {
this.load_config()
this.have_internet = (this.internet) ? true : false
this.keyboard = false
if (typeof this.list_ssids == "object" && this.list_ssids.length != 0){
this.ssids = this.list_ssids
} else {
this.refresh_wifi_list()
}
}
}
</script>