mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-04-27 07:59:59 -07:00
parity with current UI
This commit is contained in:
@@ -8,6 +8,13 @@ export type AnalysisReport = {
|
||||
|
||||
export type ReportMetadata = {
|
||||
analyzers: AnalyzerMetadata[];
|
||||
rayhunter: RayhunterMetadata;
|
||||
};
|
||||
|
||||
export type RayhunterMetadata = {
|
||||
rayhunter_version: string;
|
||||
system_os: string;
|
||||
arch: string;
|
||||
};
|
||||
|
||||
export type AnalyzerMetadata = {
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
import { AnalysisStatus } from "$lib/analysisManager.svelte";
|
||||
import { EventType } from "$lib/analysis.svelte";
|
||||
import type { ManifestEntry } from "$lib/manifest.svelte";
|
||||
let { entry }: {
|
||||
let { entry, onclick }: {
|
||||
entry: ManifestEntry,
|
||||
onclick: () => void,
|
||||
} = $props();
|
||||
|
||||
let summary = $derived.by(() => {
|
||||
@@ -32,12 +33,20 @@
|
||||
} else {
|
||||
return 'Loading...';
|
||||
}
|
||||
});
|
||||
|
||||
let ready = $derived.by(() => {
|
||||
let finished = entry.analysis_status === AnalysisStatus.Finished;
|
||||
let report_available = entry.analysis_report !== undefined;
|
||||
return finished && report_available;
|
||||
})
|
||||
|
||||
let button_class = $derived(ready ? "text-blue-400 underline" : '');
|
||||
</script>
|
||||
|
||||
<p>
|
||||
<button class={button_class} disabled={!ready} {onclick}>
|
||||
{summary}
|
||||
</p>
|
||||
</button>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
|
||||
41
bin/web/src/lib/components/AnalysisTable.svelte
Normal file
41
bin/web/src/lib/components/AnalysisTable.svelte
Normal file
@@ -0,0 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { AnalysisStatus } from "$lib/analysisManager.svelte";
|
||||
import { EventType, type AnalyzerMetadata, type ReportMetadata, type AnalysisRow, type AnalysisReport } from "$lib/analysis.svelte";
|
||||
import type { ManifestEntry } from "$lib/manifest.svelte";
|
||||
let { report }: {
|
||||
report: AnalysisReport,
|
||||
} = $props();
|
||||
|
||||
const date_formatter = new Intl.DateTimeFormat(undefined, {
|
||||
timeStyle: "long",
|
||||
dateStyle: "short",
|
||||
});
|
||||
</script>
|
||||
|
||||
<p class="text-lg underline">Warnings</p>
|
||||
<table class="table-auto text-left border">
|
||||
<thead class="p-2">
|
||||
<tr class="bg-gray-300">
|
||||
<th scope="col">Timestamp</th>
|
||||
<th scope="col">Warning</th>
|
||||
<th scope="col">Severity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each report.rows as row, row_idx}
|
||||
{#each row.analysis as analysis}
|
||||
{@const parsed_date = new Date(analysis.timestamp)}
|
||||
{@const warnings = analysis.events.filter(e => e.type === EventType.Warning)}
|
||||
{#each warnings as warning}
|
||||
{@const severity = ['Low', 'Medium', 'High'][warning.severity]}
|
||||
{@const severity_class = ['bg-red-200', 'bg-red-400', 'bg-red-600'][warning.severity]}
|
||||
<tr class="even:bg-gray-400 border-b">
|
||||
<th class="p-2">{date_formatter.format(parsed_date)}</th>
|
||||
<td class="p-2">{warning.message}</td>
|
||||
<td class="p-2 {severity_class}">{severity}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{/each}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
40
bin/web/src/lib/components/AnalysisView.svelte
Normal file
40
bin/web/src/lib/components/AnalysisView.svelte
Normal file
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import { AnalysisStatus } from "$lib/analysisManager.svelte";
|
||||
import { EventType, type AnalyzerMetadata, type ReportMetadata, type AnalysisRow } from "$lib/analysis.svelte";
|
||||
import type { ManifestEntry } from "$lib/manifest.svelte";
|
||||
import AnalysisTable from "./AnalysisTable.svelte";
|
||||
let { entry }: {
|
||||
entry: ManifestEntry,
|
||||
} = $props();
|
||||
|
||||
const date_formatter = new Intl.DateTimeFormat(undefined, {
|
||||
timeStyle: "long",
|
||||
dateStyle: "short",
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container max-h-96 overflow-auto">
|
||||
{#if entry.analysis_report === undefined}
|
||||
<p>Report unavailable, try refreshing.</p>
|
||||
{:else if typeof(entry.analysis_report) === 'string'}
|
||||
<p>Error getting analysis report: {entry.analysis_report}</p>
|
||||
{:else}
|
||||
{@const metadata: ReportMetadata = entry.analysis_report.metadata}
|
||||
<div class="flex flex-col p-2 w-3/4">
|
||||
{#if entry.analysis_report.rows.length > 0}
|
||||
<AnalysisTable report={entry.analysis_report} />
|
||||
{:else}
|
||||
<p>No warnings to display!</p>
|
||||
{/if}
|
||||
<div>
|
||||
<p class="text-lg underline">Metadata</p>
|
||||
<p><b>Rayhunter version:</b> {metadata.rayhunter.rayhunter_version}</p>
|
||||
<p><b>Device system OS:</b> {metadata.rayhunter.system_os}</p>
|
||||
<p class="text-lg underline">Analyzers</p>
|
||||
{#each metadata.analyzers as analyzer}
|
||||
<p><b>{analyzer.name}:</b> {analyzer.description}</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -8,7 +8,7 @@
|
||||
let { entries, current_entry }: Props = $props();
|
||||
</script>
|
||||
|
||||
<table class="table-auto border">
|
||||
<table class="table-auto text-left border">
|
||||
<thead class="p-2">
|
||||
<tr class="bg-gray-300">
|
||||
<th scope="col">Name</th>
|
||||
@@ -17,16 +17,16 @@
|
||||
<th scope="col">Size (bytes)</th>
|
||||
<th scope="col">PCAP</th>
|
||||
<th scope="col">QMDL</th>
|
||||
<th scope="col">Analysis Result</th>
|
||||
<th scope="col">Analysis</th>
|
||||
<th scope="col">Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#if current_entry !== undefined}
|
||||
<TableRow entry={current_entry} current={true} />
|
||||
<TableRow entry={current_entry} current={true} i={0} />
|
||||
{/if}
|
||||
{#each entries as entry}
|
||||
<TableRow entry={entry} current={false} />
|
||||
{#each entries as entry, i}
|
||||
<TableRow {entry} current={false} {i} />
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
import DownloadLink from '$lib/components/DownloadLink.svelte';
|
||||
import DeleteButton from "$lib/components/DeleteButton.svelte";
|
||||
import AnalysisStatus from "./AnalysisStatus.svelte";
|
||||
let { entry, current }: {
|
||||
import AnalysisView from "./AnalysisView.svelte";
|
||||
let { entry, current, i }: {
|
||||
entry: ManifestEntry;
|
||||
current: boolean;
|
||||
i: number
|
||||
} = $props();
|
||||
|
||||
// passing `undefined` as the locale uses the browser default
|
||||
@@ -13,17 +15,19 @@
|
||||
timeStyle: "long",
|
||||
dateStyle: "short",
|
||||
});
|
||||
let row_color = current ? "bg-green-300" : "even:bg-gray-100";
|
||||
let normal_row_color = i % 2 == 0 ? "bg-white" : "bg-gray-100";
|
||||
let row_color = current ? "bg-green-300" : normal_row_color;
|
||||
let analysis_visible = $state(false);
|
||||
</script>
|
||||
|
||||
<tr class="{row_color} border-b">
|
||||
<tr class="{row_color}">
|
||||
<th class="font-bold p-2 bg-blue-100" scope='row'>{entry.name}</th>
|
||||
<td class="p-2">{date_formatter.format(entry.start_time)}</td>
|
||||
<td class="p-2">{date_formatter.format(entry.last_message_time)}</td>
|
||||
<td class="p-2">{entry.qmdl_size_bytes}</td>
|
||||
<td class="p-2"><DownloadLink url={entry.getPcapUrl()} text="pcap" /></td>
|
||||
<td class="p-2"><DownloadLink url={entry.getQmdlUrl()} text="qmdl" /></td>
|
||||
<td class="p-2"><AnalysisStatus entry={entry} /></td>
|
||||
<td class="p-2"><AnalysisStatus onclick={() => { analysis_visible = !analysis_visible; }} entry={entry} /></td>
|
||||
{#if current}
|
||||
<td class="p-2"></td>
|
||||
{:else}
|
||||
@@ -35,3 +39,9 @@
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
<tr class="{row_color} border-b {analysis_visible ? '' : 'collapse'}">
|
||||
<td class="font-bold p-2 bg-blue-100"></td>
|
||||
<td class="border-t border-dashed p-2" colspan="7">
|
||||
<AnalysisView {entry} />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user