grapl_graph_descriptions/
process.rs1use 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}