mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-04-26 23:49:59 -07:00
frontend: handle both old and new analysis reports
Adds support for versioned analysis reports (and defaults to v1 for reports with no version).
This commit is contained in:
committed by
Cooper Quintin
parent
e81df18315
commit
7cd8835cab
@@ -1,91 +0,0 @@
|
|||||||
import { describe, it, expect } from 'vitest';
|
|
||||||
import { EventType, parse_finished_report, Severity } from './analysis.svelte';
|
|
||||||
import { type NewlineDeliminatedJson } from './ndjson';
|
|
||||||
|
|
||||||
const SAMPLE_REPORT_NDJSON: NewlineDeliminatedJson = [
|
|
||||||
{
|
|
||||||
analyzers: [
|
|
||||||
{
|
|
||||||
name: 'LTE SIB 6/7 Downgrade',
|
|
||||||
description:
|
|
||||||
'Tests for LTE cells broadcasting a SIB type 6 and 7 which include 2G/3G frequencies with higher priorities.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'IMSI Provided',
|
|
||||||
description: "Tests whether the UE's IMSI was ever provided to the cell",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Null Cipher',
|
|
||||||
description: 'Tests whether the cell suggests using a null cipher (EEA0)',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Example Analyzer',
|
|
||||||
description:
|
|
||||||
'Always returns true, if you are seeing this you are either a developer or you are about to have problems.',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timestamp: '2024-10-08T13:25:43.011689003-07:00',
|
|
||||||
skipped_message_reasons: [
|
|
||||||
'DecodingError(UperDecodeError(Error { cause: BufferTooShort, msg: "PerCodec:DecodeError:Requested Bits to decode 3, Remaining bits 1", context: [] }))',
|
|
||||||
],
|
|
||||||
analysis: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timestamp: '2024-10-08T13:25:43.480872496-07:00',
|
|
||||||
skipped_message_reasons: [],
|
|
||||||
analysis: [
|
|
||||||
{
|
|
||||||
timestamp: '2024-08-19T03:33:54.318Z',
|
|
||||||
events: [
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
{
|
|
||||||
event_type: { type: 'QualitativeWarning', severity: 'Low' },
|
|
||||||
message: 'TMSI was provided to cell',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
describe('analysis report parsing', () => {
|
|
||||||
it('parses the example analysis', () => {
|
|
||||||
const report = parse_finished_report(SAMPLE_REPORT_NDJSON);
|
|
||||||
expect(report.metadata.analyzers).toEqual([
|
|
||||||
{
|
|
||||||
name: 'LTE SIB 6/7 Downgrade',
|
|
||||||
description:
|
|
||||||
'Tests for LTE cells broadcasting a SIB type 6 and 7 which include 2G/3G frequencies with higher priorities.',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'IMSI Provided',
|
|
||||||
description: "Tests whether the UE's IMSI was ever provided to the cell",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Null Cipher',
|
|
||||||
description: 'Tests whether the cell suggests using a null cipher (EEA0)',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Example Analyzer',
|
|
||||||
description:
|
|
||||||
'Always returns true, if you are seeing this you are either a developer or you are about to have problems.',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
expect(report.rows).toHaveLength(2);
|
|
||||||
expect(report.rows[0].skipped_message_reasons).toHaveLength(1);
|
|
||||||
expect(report.rows[0].analysis).toHaveLength(0);
|
|
||||||
expect(report.rows[1].skipped_message_reasons).toHaveLength(0);
|
|
||||||
expect(report.rows[1].analysis).toHaveLength(1);
|
|
||||||
expect(report.rows[1].analysis[0].events).toHaveLength(1);
|
|
||||||
const event = report.rows[1].analysis[0].events[0];
|
|
||||||
if (event.type === EventType.Warning) {
|
|
||||||
expect(event.severity).toEqual(Severity.Low);
|
|
||||||
} else {
|
|
||||||
throw 'wrong event type';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
142
daemon/web/src/lib/analysis.svelte.spec.ts
Normal file
142
daemon/web/src/lib/analysis.svelte.spec.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { AnalysisRowType, EventType, parse_finished_report, Severity } from './analysis.svelte';
|
||||||
|
import { type NewlineDeliminatedJson } from './ndjson';
|
||||||
|
|
||||||
|
const SAMPLE_V1_REPORT_NDJSON: NewlineDeliminatedJson = [
|
||||||
|
{
|
||||||
|
analyzers: [
|
||||||
|
{
|
||||||
|
name: "Analyzer 1",
|
||||||
|
description: "A first analyzer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Analyzer 2",
|
||||||
|
description: "A second analyzer",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: '2024-10-08T13:25:43.011689003-07:00',
|
||||||
|
skipped_message_reasons: [
|
||||||
|
'The reason why the message was skipped',
|
||||||
|
],
|
||||||
|
analysis: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timestamp: '2024-10-08T13:25:43.480872496-07:00',
|
||||||
|
skipped_message_reasons: [],
|
||||||
|
analysis: [
|
||||||
|
{
|
||||||
|
timestamp: '2024-08-19T03:33:54.318Z',
|
||||||
|
events: [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
event_type: { type: 'QualitativeWarning', severity: 'Low' },
|
||||||
|
message: 'Something nasty happened',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const SAMPLE_V2_REPORT_NDJSON: NewlineDeliminatedJson = [
|
||||||
|
{
|
||||||
|
analyzers: [
|
||||||
|
{
|
||||||
|
name: "Analyzer 1",
|
||||||
|
description: "A first analyzer",
|
||||||
|
version: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Analyzer 2",
|
||||||
|
description: "A second analyzer",
|
||||||
|
version: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
report_version: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
skipped_message_reason: 'The reason why the message was skipped',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
packet_timestamp: '2024-08-19T03:33:54.318Z',
|
||||||
|
events: [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
event_type: { type: 'QualitativeWarning', severity: 'Low' },
|
||||||
|
message: 'Something nasty happened',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe('analysis report parsing', () => {
|
||||||
|
it('parses v1 example analysis', () => {
|
||||||
|
const report = parse_finished_report(SAMPLE_V1_REPORT_NDJSON);
|
||||||
|
expect(report.metadata.report_version).toEqual(1);
|
||||||
|
expect(report.metadata.analyzers).toEqual([
|
||||||
|
{
|
||||||
|
name: 'Analyzer 1',
|
||||||
|
description: "A first analyzer",
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Analyzer 2',
|
||||||
|
description: "A second analyzer",
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(report.rows).toHaveLength(2);
|
||||||
|
expect(report.rows[0].type).toBe(AnalysisRowType.Skipped);
|
||||||
|
if (report.rows[1].type === AnalysisRowType.Analysis) {
|
||||||
|
const row = report.rows[1];
|
||||||
|
expect(row.events).toHaveLength(2);
|
||||||
|
expect(row.events[0]).toBeNull();
|
||||||
|
const event = row.events[1];
|
||||||
|
const expected_timestamp = new Date('2024-08-19T03:33:54.318Z');
|
||||||
|
expect(row.packet_timestamp.getTime()).toEqual(expected_timestamp.getTime())
|
||||||
|
if (event !== null && event.type === EventType.Warning) {
|
||||||
|
expect(event.severity).toEqual(Severity.Low);
|
||||||
|
} else {
|
||||||
|
throw 'wrong event type';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw 'wrong row type'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('parses v2 example analysis', () => {
|
||||||
|
const report = parse_finished_report(SAMPLE_V2_REPORT_NDJSON);
|
||||||
|
expect(report.metadata.report_version).toEqual(2);
|
||||||
|
expect(report.metadata.analyzers).toEqual([
|
||||||
|
{
|
||||||
|
name: 'Analyzer 1',
|
||||||
|
description: "A first analyzer",
|
||||||
|
version: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Analyzer 2',
|
||||||
|
description: "A second analyzer",
|
||||||
|
version: 2,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(report.rows).toHaveLength(2);
|
||||||
|
expect(report.rows[0].type).toBe(AnalysisRowType.Skipped);
|
||||||
|
if (report.rows[1].type === AnalysisRowType.Analysis) {
|
||||||
|
const row = report.rows[1];
|
||||||
|
expect(row.events).toHaveLength(2);
|
||||||
|
expect(row.events[0]).toBeNull();
|
||||||
|
const event = row.events[1];
|
||||||
|
const expected_timestamp = new Date('2024-08-19T03:33:54.318Z');
|
||||||
|
expect(row.packet_timestamp.getTime()).toEqual(expected_timestamp.getTime())
|
||||||
|
if (event !== null && event.type === EventType.Warning) {
|
||||||
|
expect(event.severity).toEqual(Severity.Low);
|
||||||
|
} else {
|
||||||
|
throw 'wrong event type';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw 'wrong row type'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,9 +13,23 @@ export type ReportStatistics = {
|
|||||||
num_skipped_packets: number;
|
num_skipped_packets: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReportMetadata = {
|
export class ReportMetadata {
|
||||||
analyzers: AnalyzerMetadata[];
|
public analyzers: AnalyzerMetadata[];
|
||||||
rayhunter: RayhunterMetadata;
|
public rayhunter: RayhunterMetadata;
|
||||||
|
public report_version: number;
|
||||||
|
|
||||||
|
constructor(ndjson: any) {
|
||||||
|
this.analyzers = ndjson.analyzers;
|
||||||
|
this.rayhunter = ndjson.rayhunter;
|
||||||
|
if (ndjson.report_version === undefined) {
|
||||||
|
this.report_version = 1;
|
||||||
|
this.analyzers.forEach(analyzer => {
|
||||||
|
analyzer.version = 1;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.report_version = ndjson.report_version;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RayhunterMetadata = {
|
export type RayhunterMetadata = {
|
||||||
@@ -27,19 +41,27 @@ export type RayhunterMetadata = {
|
|||||||
export type AnalyzerMetadata = {
|
export type AnalyzerMetadata = {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
version: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AnalysisRow = {
|
export type AnalysisRow = SkippedPacket | PacketAnalysis;
|
||||||
timestamp: Date;
|
export enum AnalysisRowType {
|
||||||
skipped_message_reasons: string[];
|
Skipped,
|
||||||
analysis: PacketAnalysis[];
|
Analysis,
|
||||||
};
|
}
|
||||||
|
|
||||||
|
export type SkippedPacket = {
|
||||||
|
type: AnalysisRowType.Skipped;
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
|
|
||||||
export type PacketAnalysis = {
|
export type PacketAnalysis = {
|
||||||
timestamp: Date;
|
type: AnalysisRowType.Analysis;
|
||||||
|
packet_timestamp: Date;
|
||||||
events: Event[];
|
events: Event[];
|
||||||
};
|
};
|
||||||
export type Event = QualitativeWarning | InformationalEvent;
|
|
||||||
|
export type Event = QualitativeWarning | InformationalEvent | null;
|
||||||
export enum EventType {
|
export enum EventType {
|
||||||
Informational,
|
Informational,
|
||||||
Warning,
|
Warning,
|
||||||
@@ -62,56 +84,118 @@ export type InformationalEvent = {
|
|||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function parse_finished_report(report_json: NewlineDeliminatedJson): AnalysisReport {
|
function get_event(event_json: any): Event {
|
||||||
const metadata: ReportMetadata = report_json[0]; // this can be cast directly
|
if (event_json.event_type.type === 'Informational') {
|
||||||
let num_warnings = 0;
|
return {
|
||||||
let num_informational_logs = 0;
|
type: EventType.Informational,
|
||||||
let num_skipped_packets = 0;
|
message: event_json.message,
|
||||||
const rows: AnalysisRow[] = report_json.slice(1).map((row_json: any) => {
|
};
|
||||||
const analysis: PacketAnalysis[] = row_json.analysis.map((analysis_json: any) => {
|
} else {
|
||||||
|
return {
|
||||||
|
type: EventType.Warning,
|
||||||
|
severity:
|
||||||
|
event_json.event_type.severity === 'High'
|
||||||
|
? Severity.High
|
||||||
|
: event_json.event_type.severity === 'Medium'
|
||||||
|
? Severity.Medium
|
||||||
|
: Severity.Low,
|
||||||
|
message: event_json.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_v1_rows(row_jsons: any[]): AnalysisRow[] {
|
||||||
|
const rows: AnalysisRow[] = [];
|
||||||
|
for (const row_json of row_jsons) {
|
||||||
|
for (const reason of row_json.skipped_message_reasons) {
|
||||||
|
rows.push({
|
||||||
|
type: AnalysisRowType.Skipped,
|
||||||
|
reason,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (const analysis_json of row_json.analysis) {
|
||||||
const events: Event[] = analysis_json.events
|
const events: Event[] = analysis_json.events
|
||||||
.map((event_json: any): Event | null => {
|
.map((event_json: any): Event | null => {
|
||||||
if (event_json === null) {
|
if (event_json === null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (event_json.event_type.type === 'Informational') {
|
|
||||||
num_informational_logs += 1;
|
|
||||||
return {
|
|
||||||
type: EventType.Informational,
|
|
||||||
message: event_json.message,
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
num_warnings += 1;
|
return get_event(event_json);
|
||||||
return {
|
|
||||||
type: EventType.Warning,
|
|
||||||
severity:
|
|
||||||
event_json.event_type.severity === 'High'
|
|
||||||
? Severity.High
|
|
||||||
: event_json.event_type.severity === 'Medium'
|
|
||||||
? Severity.Medium
|
|
||||||
: Severity.Low,
|
|
||||||
message: event_json.message,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.filter((maybe_event: Event | null) => maybe_event !== null);
|
rows.push({
|
||||||
return {
|
type: AnalysisRowType.Analysis,
|
||||||
timestamp: analysis_json.timestamp,
|
packet_timestamp: new Date(analysis_json.timestamp),
|
||||||
events,
|
events,
|
||||||
};
|
});
|
||||||
});
|
}
|
||||||
num_skipped_packets += row_json.skipped_message_reasons.length;
|
}
|
||||||
return {
|
return rows;
|
||||||
timestamp: new Date(row_json.timestamp),
|
}
|
||||||
skipped_message_reasons: row_json.skipped_message_reasons,
|
|
||||||
analysis,
|
function get_v2_rows(row_jsons: any[]): AnalysisRow[] {
|
||||||
};
|
const rows: AnalysisRow[] = [];
|
||||||
});
|
for (const row_json of row_jsons) {
|
||||||
|
if (row_json.skipped_message_reason) {
|
||||||
|
rows.push({
|
||||||
|
type: AnalysisRowType.Skipped,
|
||||||
|
reason: row_json.skipped_message_reason,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const events: Event[] = row_json.events
|
||||||
|
.map((event_json: any): Event | null => {
|
||||||
|
if (event_json === null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return get_event(event_json);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rows.push({
|
||||||
|
type: AnalysisRowType.Analysis,
|
||||||
|
packet_timestamp: new Date(row_json.packet_timestamp),
|
||||||
|
events,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_report_stats(rows: AnalysisRow[]): ReportStatistics {
|
||||||
|
let num_warnings = 0;
|
||||||
|
let num_informational_logs = 0;
|
||||||
|
let num_skipped_packets = 0;
|
||||||
|
for (const row of rows) {
|
||||||
|
if (row.type === AnalysisRowType.Skipped) {
|
||||||
|
num_skipped_packets++;
|
||||||
|
} else {
|
||||||
|
for (const event of row.events) {
|
||||||
|
if (event !== null) {
|
||||||
|
if (event.type === EventType.Informational) {
|
||||||
|
num_informational_logs++;
|
||||||
|
} else {
|
||||||
|
num_warnings++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
statistics: {
|
num_warnings,
|
||||||
num_informational_logs,
|
num_informational_logs,
|
||||||
num_warnings,
|
num_skipped_packets,
|
||||||
num_skipped_packets,
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
export function parse_finished_report(report_json: NewlineDeliminatedJson): AnalysisReport {
|
||||||
|
const metadata = new ReportMetadata(report_json[0]);
|
||||||
|
let rows;
|
||||||
|
if (metadata.report_version === 1) {
|
||||||
|
rows = get_v1_rows(report_json.slice(1));
|
||||||
|
} else {
|
||||||
|
rows = get_v2_rows(report_json.slice(1));
|
||||||
|
}
|
||||||
|
let statistics = get_report_stats(rows);
|
||||||
|
return {
|
||||||
|
statistics,
|
||||||
metadata,
|
metadata,
|
||||||
rows,
|
rows,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AnalysisStatus } from '$lib/analysisManager.svelte';
|
import { AnalysisStatus } from '$lib/analysisManager.svelte';
|
||||||
import { EventType } from '$lib/analysis.svelte';
|
import { AnalysisRowType, EventType } from '$lib/analysis.svelte';
|
||||||
import type { ManifestEntry } from '$lib/manifest.svelte';
|
import type { ManifestEntry } from '$lib/manifest.svelte';
|
||||||
let {
|
let {
|
||||||
entry,
|
entry,
|
||||||
@@ -23,17 +23,7 @@
|
|||||||
} else if (typeof entry.analysis_report === 'string') {
|
} else if (typeof entry.analysis_report === 'string') {
|
||||||
return entry.analysis_report;
|
return entry.analysis_report;
|
||||||
} else {
|
} else {
|
||||||
let num_warnings = 0;
|
return `${entry.analysis_report.statistics.num_warnings} warnings`;
|
||||||
for (let row of entry.analysis_report.rows) {
|
|
||||||
for (let analysis of row.analysis) {
|
|
||||||
for (let event of analysis.events) {
|
|
||||||
if (event.type === EventType.Warning) {
|
|
||||||
num_warnings += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return `${num_warnings} warnings`;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 'Loading...';
|
return 'Loading...';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { EventType, type AnalysisReport } from '$lib/analysis.svelte';
|
import { AnalysisRowType, EventType, type AnalysisReport } from '$lib/analysis.svelte';
|
||||||
let {
|
let {
|
||||||
report,
|
report,
|
||||||
}: {
|
}: {
|
||||||
@@ -11,15 +11,17 @@
|
|||||||
dateStyle: 'short',
|
dateStyle: 'short',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const analyzers = report.metadata.analyzers;
|
||||||
|
|
||||||
const skipped_messages: Map<string, number> = $derived.by(() => {
|
const skipped_messages: Map<string, number> = $derived.by(() => {
|
||||||
let map = new Map();
|
let map = new Map();
|
||||||
for (const row of report.rows) {
|
for (const row of report.rows) {
|
||||||
for (const message of row.skipped_message_reasons) {
|
if (row.type === AnalysisRowType.Skipped) {
|
||||||
let count = map.get(message);
|
let count = map.get(row.reason);
|
||||||
if (count === undefined) {
|
if (count === undefined) {
|
||||||
count = 0;
|
count = 0;
|
||||||
}
|
}
|
||||||
map.set(message, count + 1);
|
map.set(row.reason, count + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
@@ -35,15 +37,17 @@
|
|||||||
<thead class="p-2">
|
<thead class="p-2">
|
||||||
<tr class="bg-gray-300">
|
<tr class="bg-gray-300">
|
||||||
<th class="p-2">Timestamp</th>
|
<th class="p-2">Timestamp</th>
|
||||||
|
<th class="p-2">Heuristic</th>
|
||||||
<th class="p-2">Warning</th>
|
<th class="p-2">Warning</th>
|
||||||
<th class="p-2">Severity</th>
|
<th class="p-2">Severity</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each report.rows as row}
|
{#each report.rows as row}
|
||||||
{#each row.analysis as analysis}
|
{#if row.type === AnalysisRowType.Analysis}
|
||||||
{@const parsed_date = new Date(analysis.timestamp)}
|
{@const parsed_date = new Date(row.packet_timestamp)}
|
||||||
{#each analysis.events.filter((e) => e !== null) as event}
|
{#each row.events.filter((e) => e !== null) as event, i}
|
||||||
|
{@const analyzer = analyzers[i]}
|
||||||
<tr class="even:bg-gray-200 odd:bg-white">
|
<tr class="even:bg-gray-200 odd:bg-white">
|
||||||
{#if event.type === EventType.Warning}
|
{#if event.type === EventType.Warning}
|
||||||
{@const severity = ['Low', 'Medium', 'High'][event.severity]}
|
{@const severity = ['Low', 'Medium', 'High'][event.severity]}
|
||||||
@@ -53,16 +57,18 @@
|
|||||||
'bg-red-600',
|
'bg-red-600',
|
||||||
][event.severity]}
|
][event.severity]}
|
||||||
<td class="p-2">{date_formatter.format(parsed_date)}</td>
|
<td class="p-2">{date_formatter.format(parsed_date)}</td>
|
||||||
|
<td class="p-2">{analyzer.name} v{analyzer.version}</td>
|
||||||
<td class="p-2">{event.message}</td>
|
<td class="p-2">{event.message}</td>
|
||||||
<td class="p-2 {severity_class} text-center">{severity}</td>
|
<td class="p-2 {severity_class} text-center">{severity}</td>
|
||||||
{:else if event.type === EventType.Informational}
|
{:else if event.type === EventType.Informational}
|
||||||
<td class="p-2">{date_formatter.format(parsed_date)}</td>
|
<td class="p-2">{date_formatter.format(parsed_date)}</td>
|
||||||
|
<td class="p-2">{analyzer.name} v{analyzer.version}</td>
|
||||||
<td class="p-2">{event.message}</td>
|
<td class="p-2">{event.message}</td>
|
||||||
<td class="p-2">Info</td>
|
<td class="p-2">Info</td>
|
||||||
{/if}
|
{/if}
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user