parity with current UI

This commit is contained in:
Will Greenberg
2025-04-15 18:08:18 -07:00
parent a33c7511eb
commit d63f419fbc
8 changed files with 120 additions and 13 deletions

View File

@@ -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 = {

View File

@@ -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>

View 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>

View 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>

View File

@@ -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>

View File

@@ -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>