grapl_graph_descriptions/
process.rs

1use std::convert::TryFrom;
2
3use log::warn;
4use serde_json::{json, Value};
5use uuid::Uuid;
6
7use crate::error::Error;
8use crate::graph_description::Process;
9use crate::node::NodeT;
10
11#[derive(Debug, Clone)]
12pub enum ProcessState {
13    Created,
14    Terminated,
15    Existing,
16}
17
18impl From<ProcessState> for u32 {
19    fn from(p: ProcessState) -> u32 {
20        match p {
21            ProcessState::Created => 1,
22            ProcessState::Terminated => 2,
23            ProcessState::Existing => 3,
24        }
25    }
26}
27
28impl TryFrom<u32> for ProcessState {
29    type Error = Error;
30
31    fn try_from(p: u32) -> Result<ProcessState, Error> {
32        match p {
33            1 => Ok(ProcessState::Created),
34            2 => Ok(ProcessState::Terminated),
35            3 => Ok(ProcessState::Existing),
36            _ => Err(Error::InvalidProcessState(p)),
37        }
38    }
39}
40
41impl Process {
42    pub fn new(
43        asset_id: impl Into<Option<String>>,
44        hostname: impl Into<Option<String>>,
45        state: ProcessState,
46        process_id: u64,
47        timestamp: u64,
48        process_name: String,
49        operating_system: String,
50        process_command_line: String,
51        process_guid: String,
52    ) -> Process {
53        let asset_id = asset_id.into();
54        let hostname = hostname.into();
55
56        if asset_id.is_none() && hostname.is_none() {
57            panic!("AssetID or Hostname must be provided for ProcessOutboundConnection");
58        }
59
60        let mut pd = Self {
61            node_key: Uuid::new_v4().to_string(),
62            asset_id: asset_id.into(),
63            hostname: hostname.into(),
64            state: state.clone().into(),
65            process_id,
66            process_name,
67            created_timestamp: 0,
68            terminated_timestamp: 0,
69            last_seen_timestamp: 0,
70            operating_system,
71            process_command_line,
72            process_guid,
73        };
74
75        match state {
76            ProcessState::Created => pd.created_timestamp = timestamp,
77            ProcessState::Existing => pd.last_seen_timestamp = timestamp,
78            ProcessState::Terminated => pd.terminated_timestamp = timestamp,
79        }
80
81        pd
82    }
83
84    pub fn into_json(self) -> Value {
85        let mut j = json!({
86            "node_key": self.node_key,
87            "process_id": self.process_id,
88            "dgraph.type": "Process"
89        });
90
91        if !self.process_name.is_empty() {
92            j["process_name"] = Value::from(self.process_name);
93        }
94
95        if !self.operating_system.is_empty() {
96            j["operating_system"] = Value::from(self.operating_system);
97        }
98
99        if !self.process_command_line.is_empty() {
100            j["process_command_line"] = Value::from(self.process_command_line);
101        }
102
103        if !self.process_guid.is_empty() {
104            j["process_guid"] = Value::from(self.process_guid);
105        }
106
107        if self.created_timestamp != 0 {
108            j["created_timestamp"] = self.created_timestamp.into()
109        }
110
111        if self.terminated_timestamp != 0 {
112            j["terminated_timestamp"] = self.terminated_timestamp.into()
113        }
114        if self.last_seen_timestamp != 0 {
115            j["last_seen_timestamp"] = self.last_seen_timestamp.into()
116        }
117
118        j
119    }
120}
121
122impl NodeT for Process {
123    fn get_asset_id(&self) -> Option<&str> {
124        self.asset_id.as_ref().map(String::as_str)
125    }
126
127    fn set_asset_id(&mut self, asset_id: impl Into<String>) {
128        self.asset_id = Some(asset_id.into())
129    }
130
131    fn get_node_key(&self) -> &str {
132        self.node_key.as_str()
133    }
134
135    fn set_node_key(&mut self, node_key: impl Into<String>) {
136        self.node_key = node_key.into();
137    }
138
139    fn merge(&mut self, other: &Self) -> bool {
140        if self.node_key != other.node_key {
141            warn!("Attempted to merge two Process Nodes with differing node_keys");
142            return false;
143        }
144
145        let mut merged = false;
146
147        if self.process_name.is_empty() && !other.process_name.is_empty() {
148            self.process_name = other.process_name.clone();
149            merged = true;
150        }
151
152        if self.operating_system.is_empty() && !other.operating_system.is_empty() {
153            self.operating_system = other.operating_system.clone();
154            merged = true;
155        }
156
157        if self.process_command_line.is_empty() && !other.process_command_line.is_empty() {
158            self.process_command_line = other.process_command_line.clone();
159            merged = true;
160        }
161
162        if self.process_guid.is_empty() && !other.process_guid.is_empty() {
163            self.process_guid = other.process_guid.clone();
164            merged = true;
165        }
166
167        if self.created_timestamp == 0 || other.created_timestamp < self.created_timestamp {
168            self.created_timestamp = other.created_timestamp;
169            merged = true;
170        }
171
172        if self.terminated_timestamp == 0 || other.terminated_timestamp > self.terminated_timestamp
173        {
174            self.terminated_timestamp = other.terminated_timestamp;
175            merged = true;
176        }
177
178        if self.last_seen_timestamp == 0 || other.last_seen_timestamp > self.last_seen_timestamp {
179            self.last_seen_timestamp = other.last_seen_timestamp;
180            merged = true;
181        }
182
183        merged
184    }
185
186    fn merge_into(&mut self, other: Self) -> bool {
187        if self.node_key != other.node_key {
188            warn!("Attempted to merge two IpPort Nodes with differing node_keys");
189            return false;
190        }
191
192        let mut merged = false;
193
194        if self.process_name.is_empty() {
195            self.process_name = other.process_name;
196            merged = true;
197        }
198
199        if self.operating_system.is_empty() {
200            self.operating_system = other.operating_system;
201            merged = true;
202        }
203
204        if self.process_command_line.is_empty() {
205            self.process_command_line = other.process_command_line;
206            merged = true;
207        }
208
209        if self.process_guid.is_empty() && !other.process_guid.is_empty() {
210            self.process_guid = other.process_guid;
211            merged = true;
212        }
213
214        if self.created_timestamp == 0 || other.created_timestamp < self.created_timestamp {
215            self.created_timestamp = other.created_timestamp;
216            merged = true;
217        }
218
219        if self.terminated_timestamp == 0 || other.terminated_timestamp > self.terminated_timestamp
220        {
221            self.terminated_timestamp = other.terminated_timestamp;
222            merged = true;
223        }
224
225        if self.last_seen_timestamp == 0 || other.last_seen_timestamp > self.last_seen_timestamp {
226            self.last_seen_timestamp = other.last_seen_timestamp;
227            merged = true;
228        }
229
230        merged
231    }
232}