bn_bdash_extras/
activity.rs

1//! A type-safe representation of a workflow activity configuration
2//!
3//! This can be passed in place of a JSON string when creating an [`binaryninja::workflow::Activity`].
4//!
5//! ```no_run
6//! # use bn_bdash_extras::activity::*;
7//! # use binaryninja::workflow::{Activity, AnalysisContext, Workflow};
8//! # fn remove_arm64e_pac(ctx: &AnalysisContext) {}
9//! # fn demo(workflow: Workflow) {
10//! let workflow = workflow.clone_to(&workflow.name());
11//! let config = Config::action(
12//!     "bdash.arm64e-pac",
13//!     "Remove explicit arm64e PAC checks",
14//!     "Remove explicit arm64e pointer authentication checks prior to tail calls",
15//! )
16//! .with_eligibility(
17//!     Eligibility::auto()
18//!         .with_predicate(ViewType::In(&["Mach-O", "DSCView", "KCView"]))
19//! );
20//! let activity = Activity::new_with_action(&config.to_string(), remove_arm64e_pac);
21//! workflow.register_activity(&activity).unwrap();
22//! # }
23//! ```
24
25#![allow(unused)]
26
27use serde_json::ser;
28
29#[derive(serde::Deserialize, serde::Serialize, Debug)]
30pub struct Config<'a> {
31    pub name: &'a str,
32    pub title: &'a str,
33    pub description: &'a str,
34    #[serde(default)]
35    pub role: Role,
36    #[serde(default)]
37    pub eligibility: Eligibility,
38}
39
40impl<'a> Config<'a> {
41    #[must_use]
42    pub fn action(name: &'a str, title: &'a str, description: &'a str) -> Self {
43        Self {
44            name,
45            title,
46            description,
47            role: Role::Action,
48            eligibility: Eligibility::default(),
49        }
50    }
51
52    #[must_use]
53    pub fn with_eligibility(mut self, eligibility: Eligibility) -> Self {
54        self.eligibility = eligibility;
55        self
56    }
57}
58
59impl std::fmt::Display for Config<'_> {
60    /// Serializes the config to a JSON string.
61    ///
62    /// # Panics
63    ///
64    /// Panics if serialization to JSON fails.
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        let s = serde_json::to_string(self).expect("Failed to serialize config to JSON");
67        write!(f, "{s}")
68    }
69}
70
71#[derive(serde::Deserialize, serde::Serialize, Debug)]
72#[serde(rename_all = "lowercase")]
73#[derive(Default)]
74pub enum Role {
75    #[default]
76    Action,
77    Selector,
78    Subflow,
79    Task,
80}
81
82#[derive(serde::Deserialize, serde::Serialize, Debug)]
83#[serde(rename_all = "camelCase")]
84pub struct Eligibility {
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub auto: Option<Auto>,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub run_once: Option<bool>,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub run_once_per_session: Option<bool>,
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub continuation: Option<bool>,
93    #[serde(skip_serializing_if = "Vec::is_empty")]
94    pub predicates: Vec<Predicate>,
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub logical_operator: Option<PredicateLogicalOperator>,
97}
98
99impl Eligibility {
100    #[must_use]
101    pub fn auto() -> Self {
102        Eligibility {
103            auto: Some(Auto::new()),
104            run_once: None,
105            run_once_per_session: None,
106            continuation: None,
107            predicates: vec![],
108            logical_operator: None,
109        }
110    }
111
112    #[must_use]
113    pub fn auto_with_default(value: bool) -> Self {
114        Eligibility {
115            auto: Some(Auto::new().default(value)),
116            run_once: None,
117            run_once_per_session: None,
118            continuation: None,
119            predicates: vec![],
120            logical_operator: None,
121        }
122    }
123
124    #[must_use]
125    pub fn with_predicate<P: Into<Predicate>>(mut self, predicate: P) -> Self {
126        self.predicates = vec![predicate.into()];
127        self
128    }
129
130    #[must_use]
131    pub fn matching_any_predicate(mut self, predicates: &[Predicate]) -> Self {
132        self.predicates = predicates.to_vec();
133        self.logical_operator = Some(PredicateLogicalOperator::Or);
134        self
135    }
136
137    #[must_use]
138    pub fn matching_all_predicates(mut self, predicates: &[Predicate]) -> Self {
139        self.predicates = predicates.to_vec();
140        self.logical_operator = Some(PredicateLogicalOperator::And);
141        self
142    }
143}
144
145impl Default for Eligibility {
146    fn default() -> Self {
147        Self::auto()
148    }
149}
150
151#[derive(serde::Deserialize, serde::Serialize, Debug, Default)]
152pub struct Auto {
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub default: Option<bool>,
155}
156
157impl Auto {
158    #[must_use]
159    pub fn new() -> Self {
160        Self { default: None }
161    }
162
163    #[must_use]
164    pub fn default(mut self, value: bool) -> Self {
165        self.default = Some(value);
166        self
167    }
168}
169
170#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
171pub struct Predicate {
172    #[serde(flatten)]
173    pub predicate_type: PredicateType,
174    pub operator: Operator,
175    pub value: serde_json::Value,
176}
177
178impl Predicate {
179    pub fn setting<I: ToString>(
180        identifier: &I,
181        operator: Operator,
182        value: impl serde::Serialize,
183    ) -> Self {
184        Self {
185            predicate_type: PredicateType::Setting {
186                identifier: identifier.to_string(),
187            },
188            operator,
189            value: serde_json::json!(value),
190        }
191    }
192}
193
194pub enum ViewType<'a> {
195    In(&'a [&'a str]),
196    NotIn(&'a [&'a str]),
197}
198
199impl From<ViewType<'_>> for Predicate {
200    fn from(predicate: ViewType) -> Self {
201        match predicate {
202            ViewType::In(value) => Predicate {
203                predicate_type: PredicateType::ViewType,
204                operator: Operator::In,
205                value: serde_json::json!(value),
206            },
207            ViewType::NotIn(value) => Predicate {
208                predicate_type: PredicateType::ViewType,
209                operator: Operator::NotIn,
210                value: serde_json::json!(value),
211            },
212        }
213    }
214}
215
216pub struct Setting {
217    identifier: String,
218    operator: Operator,
219    value: serde_json::Value,
220}
221
222impl Setting {
223    pub fn new(
224        identifier: &impl ToString,
225        operator: Operator,
226        value: impl serde::Serialize,
227    ) -> Self {
228        Self {
229            identifier: identifier.to_string(),
230            operator,
231            value: serde_json::json!(value),
232        }
233    }
234
235    pub fn eq(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
236        Self::new(identifier, Operator::Eq, value)
237    }
238
239    pub fn ne(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
240        Self::new(identifier, Operator::Ne, value)
241    }
242
243    pub fn lt(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
244        Self::new(identifier, Operator::Lt, value)
245    }
246
247    pub fn lte(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
248        Self::new(identifier, Operator::Lte, value)
249    }
250
251    pub fn gt(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
252        Self::new(identifier, Operator::Gt, value)
253    }
254
255    pub fn gte(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
256        Self::new(identifier, Operator::Gte, value)
257    }
258
259    pub fn in_(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
260        Self::new(identifier, Operator::In, value)
261    }
262
263    pub fn not_in(identifier: &impl ToString, value: impl serde::Serialize) -> Self {
264        Self::new(identifier, Operator::NotIn, value)
265    }
266}
267
268impl From<Setting> for Predicate {
269    fn from(setting: Setting) -> Self {
270        Predicate {
271            predicate_type: PredicateType::Setting {
272                identifier: setting.identifier,
273            },
274            operator: setting.operator,
275            value: setting.value,
276        }
277    }
278}
279
280#[derive(serde::Deserialize, serde::Serialize, Debug, Clone)]
281#[serde(rename_all = "camelCase", tag = "type")]
282pub enum PredicateType {
283    Setting { identifier: String },
284    ViewType,
285}
286
287#[derive(serde::Deserialize, serde::Serialize, Debug, Copy, Clone)]
288pub enum Operator {
289    #[serde(rename = "==")]
290    Eq,
291    #[serde(rename = "!=")]
292    Ne,
293    #[serde(rename = "<")]
294    Lt,
295    #[serde(rename = "<=")]
296    Lte,
297    #[serde(rename = ">")]
298    Gt,
299    #[serde(rename = ">=")]
300    Gte,
301    #[serde(rename = "in")]
302    In,
303    #[serde(rename = "not in")]
304    NotIn,
305}
306
307#[derive(serde::Deserialize, serde::Serialize, Debug, Clone, Copy)]
308#[serde(rename_all = "lowercase")]
309pub enum PredicateLogicalOperator {
310    And,
311    Or,
312}