///|
/// A normalized transaction used by every detector in the library.
pub(all) struct Transaction {
id : String
customer_id : String
account_id : String
amount : Int
currency : String
region : String
occurred_at : Int
}
///|
pub fn Transaction::new(
id~ : String,
customer_id~ : String,
account_id~ : String,
amount~ : Int,
currency~ : String,
region~ : String,
occurred_at~ : Int,
) -> Transaction {
{ id, customer_id, account_id, amount, currency, region, occurred_at }
}
///|
pub fn Transaction::is_debit(self : Transaction) -> Bool {
self.amount >= 0
}
///|
pub fn Transaction::age_at(self : Transaction, now : Int) -> Int {
if now < self.occurred_at {
0
} else {
now - self.occurred_at
}
}
///|
pub fn Transaction::same_customer(
self : Transaction,
other : Transaction,
) -> Bool {
self.customer_id == other.customer_id
}
///|
pub fn Transaction::same_account(
self : Transaction,
other : Transaction,
) -> Bool {
self.account_id == other.account_id
}
///|
pub fn Transaction::within(self : Transaction, start : Int, end : Int) -> Bool {
self.occurred_at >= start && self.occurred_at <= end
}
///|
pub enum AlertSeverity {
Info
Low
Medium
High
Critical
} derive(Eq, Debug)
///|
pub fn AlertSeverity::rank(self : AlertSeverity) -> Int {
match self {
Info => 0
Low => 1
Medium => 2
High => 3
Critical => 4
}
}
///|
pub fn AlertSeverity::from_score(score : Int) -> AlertSeverity {
if score >= 90 {
Critical
} else if score >= 70 {
High
} else if score >= 40 {
Medium
} else if score > 0 {
Low
} else {
Info
}
}
///|
pub enum AlertStatus {
Open
Triaged
Escalated
Resolved
Dismissed
} derive(Eq, Debug)
///|
pub fn AlertStatus::is_terminal(self : AlertStatus) -> Bool {
match self {
Open => false
Triaged => false
Escalated => false
Resolved => true
Dismissed => true
}
}
///|
pub fn AlertStatus::from_stage(stage : Int) -> AlertStatus {
if stage <= 0 {
Open
} else if stage == 1 {
Triaged
} else if stage == 2 {
Escalated
} else if stage == 3 {
Resolved
} else {
Dismissed
}
}
///|
pub(all) struct Alert {
rule_id : String
transaction_id : String
evidence : Array[String]
score : Int
severity : AlertSeverity
status : AlertStatus
}
///|
pub fn Alert::new(
rule_id : String,
transaction_id : String,
evidence : Array[String],
) -> Alert {
{ rule_id, transaction_id, evidence, score: 0, severity: Info, status: Open }
}
///|
pub fn Alert::with_score(self : Alert, score : Int) -> Alert {
{ ..self, score, severity: AlertSeverity::from_score(score) }
}
///|
pub fn Alert::transition(self : Alert, status : AlertStatus) -> Alert {
{ ..self, status, }
}
///|
pub fn Alert::has_evidence(self : Alert) -> Bool {
self.evidence.length() > 0
}
///|
pub fn Alert::is_actionable(self : Alert) -> Bool {
self.status != Resolved &&
self.status != Dismissed &&
self.severity.rank() >= 2
}