feat(web): surface bgp_prefix and rpki_status in AttackerDetail and export
AttackerData type gets bgp_prefix / rpki_status / rpki_source. TimelineSection renders prefix inline next to AS number; RPKI status shows as a green RPKI VALID / red RPKI INVALID badge, or dim NO ROA for not-found. rpki-status-badge CSS added to Dashboard.css. Export network block extended with the three new fields.
This commit is contained in:
@@ -40,6 +40,10 @@ def _shape_observation(row: dict) -> dict:
|
|||||||
"network": {
|
"network": {
|
||||||
"asn": row.get("asn"),
|
"asn": row.get("asn"),
|
||||||
"as_name": row.get("as_name"),
|
"as_name": row.get("as_name"),
|
||||||
|
"bgp_prefix": row.get("bgp_prefix"),
|
||||||
|
"asn_source": row.get("asn_source"),
|
||||||
|
"rpki_status": row.get("rpki_status"),
|
||||||
|
"rpki_source": row.get("rpki_source"),
|
||||||
"ptr_record": row.get("ptr_record"),
|
"ptr_record": row.get("ptr_record"),
|
||||||
},
|
},
|
||||||
"threat_intel": {
|
"threat_intel": {
|
||||||
|
|||||||
@@ -164,6 +164,11 @@ export const TimelineSection: React.FC<Props> = ({ attacker, open, onToggle }) =
|
|||||||
{attacker.asn != null ? (
|
{attacker.asn != null ? (
|
||||||
<span className="matrix-text">
|
<span className="matrix-text">
|
||||||
AS{attacker.asn}
|
AS{attacker.asn}
|
||||||
|
{attacker.bgp_prefix && (
|
||||||
|
<span className="dim" style={{ marginLeft: 6, fontSize: '0.75rem', fontFamily: 'monospace' }}>
|
||||||
|
{attacker.bgp_prefix}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
{attacker.as_name && (
|
{attacker.as_name && (
|
||||||
<span className="dim" style={{ marginLeft: 6, fontSize: '0.75rem' }}>
|
<span className="dim" style={{ marginLeft: 6, fontSize: '0.75rem' }}>
|
||||||
{attacker.as_name}
|
{attacker.as_name}
|
||||||
@@ -174,6 +179,15 @@ export const TimelineSection: React.FC<Props> = ({ attacker, open, onToggle }) =
|
|||||||
({attacker.asn_source})
|
({attacker.asn_source})
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
{attacker.rpki_status === 'valid' && (
|
||||||
|
<span className="rpki-status-badge valid" style={{ marginLeft: 8 }}>RPKI VALID</span>
|
||||||
|
)}
|
||||||
|
{attacker.rpki_status === 'invalid' && (
|
||||||
|
<span className="rpki-status-badge invalid" style={{ marginLeft: 8 }}>RPKI INVALID</span>
|
||||||
|
)}
|
||||||
|
{attacker.rpki_status === 'not-found' && (
|
||||||
|
<span className="dim" style={{ marginLeft: 8, fontSize: '0.7rem' }}>RPKI NO ROA</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="dim">unknown</span>
|
<span className="dim">unknown</span>
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ export interface AttackerData {
|
|||||||
country_source: string | null;
|
country_source: string | null;
|
||||||
asn: number | null;
|
asn: number | null;
|
||||||
as_name: string | null;
|
as_name: string | null;
|
||||||
|
bgp_prefix: string | null;
|
||||||
asn_source: string | null;
|
asn_source: string | null;
|
||||||
|
rpki_status: string | null;
|
||||||
|
rpki_source: string | null;
|
||||||
ptr_record: string | null;
|
ptr_record: string | null;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
behavior: AttackerBehavior | null;
|
behavior: AttackerBehavior | null;
|
||||||
|
|||||||
@@ -514,6 +514,22 @@
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rpki-status-badge {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
padding: 2px 8px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
.rpki-status-badge.valid {
|
||||||
|
border: 1px solid var(--matrix);
|
||||||
|
background: var(--matrix-tint-5);
|
||||||
|
color: var(--matrix);
|
||||||
|
}
|
||||||
|
.rpki-status-badge.invalid {
|
||||||
|
border: 1px solid var(--alert);
|
||||||
|
background: var(--alert-tint-10);
|
||||||
|
color: var(--alert);
|
||||||
|
}
|
||||||
|
|
||||||
.back-button {
|
.back-button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
6
decnet_web/src/test/fixtures/attacker.ts
vendored
6
decnet_web/src/test/fixtures/attacker.ts
vendored
@@ -19,7 +19,10 @@ export interface AttackerFixture {
|
|||||||
country_source: string | null;
|
country_source: string | null;
|
||||||
asn: number | null;
|
asn: number | null;
|
||||||
as_name: string | null;
|
as_name: string | null;
|
||||||
|
bgp_prefix: string | null;
|
||||||
asn_source: string | null;
|
asn_source: string | null;
|
||||||
|
rpki_status: string | null;
|
||||||
|
rpki_source: string | null;
|
||||||
ptr_record: string | null;
|
ptr_record: string | null;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
behavior: null;
|
behavior: null;
|
||||||
@@ -61,7 +64,10 @@ export const makeAttacker = (overrides: Partial<AttackerFixture> = {}): Attacker
|
|||||||
country_source: 'maxmind',
|
country_source: 'maxmind',
|
||||||
asn: 64500,
|
asn: 64500,
|
||||||
as_name: 'EXAMPLE-AS',
|
as_name: 'EXAMPLE-AS',
|
||||||
|
bgp_prefix: null,
|
||||||
asn_source: 'maxmind',
|
asn_source: 'maxmind',
|
||||||
|
rpki_status: null,
|
||||||
|
rpki_source: null,
|
||||||
ptr_record: null,
|
ptr_record: null,
|
||||||
updated_at: '2026-05-09T11:00:00Z',
|
updated_at: '2026-05-09T11:00:00Z',
|
||||||
behavior: null,
|
behavior: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user