Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
`hostFqdn`, and the `agents` argument on the `trafficFilterList` query and
the `applyTrafficFilterRules` mutation is now `hostFqdns`.
- `BlocklistKerberos.clientName` / `serviceName` are now `cname` / `sname`.
- Renamed `startTime` / `endTime` on the `PortScan`, `MultiHostPortScan`,
`ExternalDdos`, `FtpBruteForce`, `LdapBruteForce`, `RdpBruteForce`, and
`RepeatedHttpSessions` GraphQL types to `firstEventStartTime` /
`lastEventStartTime`. These fields are the session start times of the first
and last raw events used for detection. This is a breaking schema change;
clients must migrate to the new field names.
- Renamed `startTime` / `endTime` on `UnusualDestinationPattern` to
`samplingWindowStartTime` / `samplingWindowEndTime`, the boundaries of the
anomaly sampling window used for detection. This is a breaking schema
change; clients must migrate to the new field names.

### Added

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ num-traits = "0.2"
reqwest = { version = "0.12", default-features = false, features = [
"rustls-tls-native-roots",
] }
review-database = { git = "https://github.com/aicers/review-database.git", rev = "1e943dc" }
review-database = { git = "https://github.com/aicers/review-database.git", rev = "644db00" }
roxy = { git = "https://github.com/aicers/roxy.git", tag = "0.6.0" }
rustls = { version = "0.23", default-features = false, features = [
"ring",
Expand Down
136 changes: 127 additions & 9 deletions src/graphql/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2272,14 +2272,15 @@ mod tests {
.unwrap()
.and_local_timezone(Utc)
.unwrap();
let first_event_start_time = ts - chrono::Duration::minutes(5);
let fields = MultiHostPortScanFields {
sensor: "sensor1".to_string(),
orig_addr: Ipv4Addr::from(1).into(),
resp_port: 443,
resp_addrs: vec![Ipv4Addr::from(2).into(), Ipv4Addr::from(3).into()],
proto: 6,
start_time: ts.timestamp_nanos_opt().unwrap(),
end_time: ts.timestamp_nanos_opt().unwrap(),
first_event_start_time: first_event_start_time.timestamp_nanos_opt().unwrap(),
last_event_start_time: ts.timestamp_nanos_opt().unwrap(),
confidence: 0.8,
category: Some(EventCategory::CommandAndControl),
};
Expand All @@ -2294,15 +2295,116 @@ mod tests {

let query = format!(
"{{ event(id: \"{key}\") {{ \
... on MultiHostPortScan {{ origCountry respCountries }} \
... on MultiHostPortScan {{ \
origCountry \
respCountries \
firstEventStartTime \
lastEventStartTime \
}} \
}} }}"
);
let res = schema.execute_as_system_admin(&query).await;

let data = res.data.to_string();
assert!(
data.contains(&format!(
"firstEventStartTime: \"{}\"",
first_event_start_time.to_rfc3339()
)),
"data: {data}"
);
assert!(
data.contains(&format!("lastEventStartTime: \"{}\"", ts.to_rfc3339())),
"data: {data}"
);
assert!(data.contains(r#"origCountry: "ZZ""#), "data: {data}");
assert!(
data.contains(r#"respCountries: ["ZZ", "ZZ"]"#),
"data: {data}"
);
}

#[tokio::test]
async fn multi_raw_event_types_expose_precise_time_fields() {
let schema = TestSchema::new().await;
let type_names = [
"PortScan",
"MultiHostPortScan",
"ExternalDdos",
"FtpBruteForce",
"LdapBruteForce",
"RdpBruteForce",
"RepeatedHttpSessions",
];
let selections = type_names
.iter()
.enumerate()
.map(|(index, type_name)| {
format!(
"eventType{index}: __type(name: \"{type_name}\") {{ fields {{ name description }} }}"
)
})
.collect::<Vec<_>>()
.join(" ");
let res = schema
.execute_as_system_admin(&format!("{{ {selections} }}"))
.await;
assert!(res.errors.is_empty(), "errors: {:?}", res.errors);
let data = res.data.into_json().unwrap();

for (index, type_name) in type_names.iter().enumerate() {
let fields = data[format!("eventType{index}")]["fields"]
.as_array()
.expect("fields array");
let field = |name| fields.iter().find(|field| field["name"] == name);
assert_eq!(
field("firstEventStartTime").map(|value| &value["description"]),
Some(&serde_json::json!(
"Session start time of the first raw event used for detection."
)),
"type: {type_name}"
);
assert_eq!(
field("lastEventStartTime").map(|value| &value["description"]),
Some(&serde_json::json!(
"Session start time of the last raw event used for detection."
)),
"type: {type_name}"
);
assert!(field("startTime").is_none(), "type: {type_name}");
assert!(field("endTime").is_none(), "type: {type_name}");
}
}

#[tokio::test]
async fn unusual_destination_pattern_exposes_sampling_window_times() {
let schema = TestSchema::new().await;
let res = schema
.execute_as_system_admin(
r#"{ __type(name: "UnusualDestinationPattern") {
fields { name description }
} }"#,
)
.await;
assert!(res.errors.is_empty(), "errors: {:?}", res.errors);
let data = res.data.into_json().unwrap();
let fields = data["__type"]["fields"].as_array().expect("fields array");
let field = |name| fields.iter().find(|field| field["name"] == name);

assert_eq!(
res.data.to_string(),
r#"{event: {origCountry: "ZZ", respCountries: ["ZZ", "ZZ"]}}"#
field("samplingWindowStartTime").map(|value| &value["description"]),
Some(&serde_json::json!(
"Start boundary of the anomaly sampling window used for detection."
))
);
assert_eq!(
field("samplingWindowEndTime").map(|value| &value["description"]),
Some(&serde_json::json!(
"End boundary of the anomaly sampling window used for detection."
))
);
assert!(field("startTime").is_none());
assert!(field("endTime").is_none());
}

#[tokio::test]
Expand Down Expand Up @@ -4469,13 +4571,13 @@ mod tests {
.unwrap()
.and_local_timezone(Utc)
.unwrap();
let start_time = NaiveDate::from_ymd_opt(2018, 1, 26)
let sampling_window_start_time = NaiveDate::from_ymd_opt(2018, 1, 26)
.unwrap()
.and_hms_micro_opt(18, 0, 0, 0)
.unwrap()
.and_local_timezone(Utc)
.unwrap();
let end_time = NaiveDate::from_ymd_opt(2018, 1, 26)
let sampling_window_end_time = NaiveDate::from_ymd_opt(2018, 1, 26)
.unwrap()
.and_hms_micro_opt(18, 30, 0, 0)
.unwrap()
Expand All @@ -4484,8 +4586,8 @@ mod tests {

let fields = UnusualDestinationPatternFields {
sensor: "sensor1".to_string(),
start_time: start_time.timestamp_nanos_opt().unwrap(),
end_time: end_time.timestamp_nanos_opt().unwrap(),
sampling_window_start_time: sampling_window_start_time.timestamp_nanos_opt().unwrap(),
sampling_window_end_time: sampling_window_end_time.timestamp_nanos_opt().unwrap(),
destination_ips: vec![
Ipv4Addr::new(192, 168, 1, 1).into(),
Ipv4Addr::new(192, 168, 1, 2).into(),
Expand Down Expand Up @@ -4513,6 +4615,8 @@ mod tests {
node {{ \
... on UnusualDestinationPattern {{ \
sensor \
samplingWindowStartTime \
samplingWindowEndTime \
respAddrs \
count \
expectedMean \
Expand All @@ -4531,6 +4635,20 @@ mod tests {
let res = schema.execute_as_system_admin(&query).await;
let data = res.data.to_string();
assert!(data.contains("sensor1"));
assert!(
data.contains(&format!(
"samplingWindowStartTime: \"{}\"",
sampling_window_start_time.to_rfc3339()
)),
"data: {data}"
);
assert!(
data.contains(&format!(
"samplingWindowEndTime: \"{}\"",
sampling_window_end_time.to_rfc3339()
)),
"data: {data}"
);
assert!(data.contains("192.168.1.1"));
assert!(data.contains("192.168.1.2"));
assert!(data.contains("192.168.1.3"));
Expand Down
36 changes: 18 additions & 18 deletions src/graphql/event/conn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@ impl PortScan {
self.inner.proto
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Session start time of the first raw event used for detection.
async fn first_event_start_time(&self) -> DateTime<Utc> {
self.inner.first_event_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// Session start time of the last raw event used for detection.
async fn last_event_start_time(&self) -> DateTime<Utc> {
self.inner.last_event_start_time
}

/// Confidence
Expand Down Expand Up @@ -237,14 +237,14 @@ impl MultiHostPortScan {
self.inner.proto
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Session start time of the first raw event used for detection.
async fn first_event_start_time(&self) -> DateTime<Utc> {
self.inner.first_event_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// Session start time of the last raw event used for detection.
async fn last_event_start_time(&self) -> DateTime<Utc> {
self.inner.last_event_start_time
}

/// Confidence
Expand Down Expand Up @@ -373,14 +373,14 @@ impl ExternalDdos {
self.inner.proto
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Session start time of the first raw event used for detection.
async fn first_event_start_time(&self) -> DateTime<Utc> {
self.inner.first_event_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// Session start time of the last raw event used for detection.
async fn last_event_start_time(&self) -> DateTime<Utc> {
self.inner.last_event_start_time
}

/// Confidence
Expand Down
12 changes: 6 additions & 6 deletions src/graphql/event/ftp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,14 +161,14 @@ impl FtpBruteForce {
&self.inner.user_list
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Session start time of the first raw event used for detection.
async fn first_event_start_time(&self) -> DateTime<Utc> {
self.inner.first_event_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// Session start time of the last raw event used for detection.
async fn last_event_start_time(&self) -> DateTime<Utc> {
self.inner.last_event_start_time
}

/// Is Internal
Expand Down
12 changes: 6 additions & 6 deletions src/graphql/event/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,14 +376,14 @@ impl RepeatedHttpSessions {
self.inner.proto
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Session start time of the first raw event used for detection.
async fn first_event_start_time(&self) -> DateTime<Utc> {
self.inner.first_event_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// Session start time of the last raw event used for detection.
async fn last_event_start_time(&self) -> DateTime<Utc> {
self.inner.last_event_start_time
}

/// Responder Country
Expand Down
12 changes: 6 additions & 6 deletions src/graphql/event/ldap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,14 @@ impl LdapBruteForce {
.collect()
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Session start time of the first raw event used for detection.
async fn first_event_start_time(&self) -> DateTime<Utc> {
self.inner.first_event_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// Session start time of the last raw event used for detection.
async fn last_event_start_time(&self) -> DateTime<Utc> {
self.inner.last_event_start_time
}

/// Confidence
Expand Down
12 changes: 6 additions & 6 deletions src/graphql/event/rdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ impl RdpBruteForce {
self.inner.proto
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Session start time of the first raw event used for detection.
async fn first_event_start_time(&self) -> DateTime<Utc> {
self.inner.first_event_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// Session start time of the last raw event used for detection.
async fn last_event_start_time(&self) -> DateTime<Utc> {
self.inner.last_event_start_time
}

/// Confidence
Expand Down
12 changes: 6 additions & 6 deletions src/graphql/event/unusual_destination_pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ impl UnusualDestinationPattern {
&self.inner.sensor
}

/// Detection Start Time
async fn start_time(&self) -> DateTime<Utc> {
self.inner.start_time
/// Start boundary of the anomaly sampling window used for detection.
async fn sampling_window_start_time(&self) -> DateTime<Utc> {
self.inner.sampling_window_start_time
}

/// Detection End Time
async fn end_time(&self) -> DateTime<Utc> {
self.inner.end_time
/// End boundary of the anomaly sampling window used for detection.
async fn sampling_window_end_time(&self) -> DateTime<Utc> {
self.inner.sampling_window_end_time
}

/// Responder IP (Address) List
Expand Down
Loading