1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]

// reexport dependencies
pub use anyhow;
pub use falco_plugin_api as api;
pub use schemars;
pub use serde;

/// Mark a struct type as a table value
///
/// Tables in Falco plugins are effectively maps from a [key](`tables::TableData`)
/// to a (possibly dynamic) struct of values.
///
/// The default implementation for tables ([`tables::DynamicFieldValues`]) uses
/// dynamic fields only, but with this macro you can also define structs containing static
/// (predefined) fields that are accessible to your plugin without going through the Falco
/// plugin API.
///
/// A table can be fully static (no dynamic fields allowed). In this case, it must be tagged
/// with a `#[static_only]` attribute (to prevent accidental omission of the dynamic field values,
/// which would only get caught at runtime, possibly much later).
///
/// Alternatively, it can mark a single field as `#[dynamic]`. That field needs to implement
/// [`tables::TableValues`] and it will generally be of type [`tables::DynamicFieldValues`].
///
/// Fields tagged as `#[readonly]` won't be writable via the Falco API and fields tagged
/// as `#[hidden]` won't be exposed to the API at all. This is useful if you want to store data
/// that's incompatible with the Falco plugin API in your table.
///
/// # Example
/// ```
/// use std::ffi::CString;
/// use falco_plugin::tables::DynamicFieldValues;
/// use falco_plugin::TableValues;
///
/// #[derive(TableValues, Default)]     // all table structs must implement Default
/// #[static_only]                      // no dynamic fields in this one
/// struct TableWithStaticFieldsOnly {
///     #[readonly]
///     int_field: u64,                 // this field cannot be modified with the Falco API
///     string_field: CString,
///
///     #[hidden]
///     secret: Vec<u8>,                // this field is not visible via the Falco API
/// }
///
/// #[derive(TableValues, Default)]
/// struct AnotherTable {
///     #[readonly]
///     int_field: u64,                 // this field cannot be modified with the Falco API
///     string_field: CString,
///
///     #[hidden]
///     secret: Vec<u8>,                // this field is not visible via the Falco API
///
///     #[dynamic]
///     dynamic_fields: DynamicFieldValues, // dynamically added fields have their values here
/// }
/// ```
pub use falco_plugin_derive::TableValues;

pub use crate::plugin::error::FailureReason;
pub use crate::plugin::event::EventInputExt;

/// # The common foundation for all Falco plugins
///
/// All plugins must implement the [`base::Plugin`] trait which specifies some basic metadata
/// about the plugin. For example, a plugin that doesn't support any capabilities (which is
/// useless and would fail to load, but is a necessary step to building an actually useful plugin)
/// might look like:
///
/// ```
/// use std::ffi::CStr;
/// use falco_plugin::base::{InitInput, Plugin};
/// use falco_plugin::plugin;
/// use falco_plugin::FailureReason;
///
/// // define the type holding the plugin state
/// struct NoOpPlugin;
///
/// // implement the base::Plugin trait
/// impl Plugin for NoOpPlugin {
///     const NAME: &'static CStr = c"sample-plugin-rs";
///     const PLUGIN_VERSION: &'static CStr = c"0.0.1";
///     const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
///     const CONTACT: &'static CStr = c"you@example.com";
///     type ConfigType = ();
///
///     fn new(input: &InitInput, config: Self::ConfigType)
///         -> Result<Self, FailureReason> {
///         Ok(NoOpPlugin)
///     }
///
///     fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
///         Ok(())
///     }
/// }
///
/// // generate the actual plugin wrapper code
/// plugin!(NoOpPlugin);
/// ```
///
/// See the [`base::Plugin`] trait documentation for details.
pub mod base {
    /// The plugin init input from the Falco plugin framework
    ///
    /// The notable thing about this type is that it implements [`TableInitInput`]. You should not
    /// need to access its fields directly.
    pub use falco_plugin_api::ss_plugin_init_input as InitInput;

    pub use crate::plugin::base::wrappers::PluginApi;
    pub use crate::plugin::base::wrappers::PluginApiWithVersionOverride;
    pub use crate::plugin::base::Plugin;
    pub use crate::plugin::schema::Json;
    pub use crate::plugin::tables::ffi::InitInput as TableInitInput;
}

/// # Field extraction plugin support
///
/// Plugins with field extraction capability have the ability to extract information from events
/// based on fields. For example, a field (e.g. `proc.name`) extracts a value (e.g. process name
/// like `nginx`) from a syscall event. The plugin returns a set of supported fields, and there are
/// functions to extract a value given an event and field. The plugin framework can then build
/// filtering expressions (e.g. rule conditions) based on these fields combined with relational
/// and/or logical operators.
///
/// For example, given the expression `ct.name=root and ct.region=us-east-1`,
/// the plugin framework handles parsing the expression, calling the plugin to extract values for
/// fields `ct.name`/`ct.region` for a given event, and determining the result of the expression.
/// In a Falco output string like `An EC2 Node was created (name=%ct.name region=%ct.region)`,
/// the plugin framework handles parsing the output string, calling the plugin to extract values
/// for fields, and building the resolved string, replacing the template field names
/// (e.g. `%ct.region`) with values (e.g. `us-east-1`).
///
/// Plugins with this capability only focus on field extraction from events generated by other
/// plugins or by the core libraries. They do not provide an event source but can extract fields
/// from other event sources. The supported field extraction can be generic or be tied to a specific
/// event source. An example is JSON field extraction, where a plugin might be able to extract
/// fields from generic JSON payloads.
///
/// For your plugin to support field extraction, you will need to implement the [`extract::ExtractPlugin`]
/// trait and invoke the [`extract_plugin`] macro, for example:
///
/// ```
/// use std::ffi::{CStr, CString};
/// use anyhow::Error;
/// use falco_event::events::types::EventType;
/// use falco_plugin::base::{InitInput, Plugin};
/// use falco_plugin::{extract_plugin, FailureReason, plugin};
/// use falco_plugin::extract::{
///     EventInput,
///     ExtractFieldInfo,
///     ExtractFieldRequestArg,
///     ExtractPlugin,
///     field};
/// use falco_plugin::tables::TableReader;
///
/// struct MyExtractPlugin;
/// impl Plugin for MyExtractPlugin {
///     // ...
/// #    const NAME: &'static CStr = c"sample-plugin-rs";
/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
/// #    const CONTACT: &'static CStr = c"you@example.com";
/// #    type ConfigType = ();
/// #
/// #    fn new(input: &InitInput, config: Self::ConfigType)
/// #        -> Result<Self, FailureReason> {
/// #        Ok(MyExtractPlugin)
/// #    }
/// #
/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
/// #        Ok(())
/// #    }
/// }
///
/// impl MyExtractPlugin { // note this is not the trait implementation
///     fn extract_sample(
///         &mut self,
///         _context: &mut (),
///         _arg: ExtractFieldRequestArg,
///         _input: &EventInput,
///         _tables: &TableReader,
///     ) -> Result<CString, Error> {
///         Ok(c"hello".to_owned())
///     }
/// }
///
/// impl ExtractPlugin for MyExtractPlugin {
///     const EVENT_TYPES: &'static [EventType] = &[]; // all event types
///     const EVENT_SOURCES: &'static [&'static str] = &[]; // all event sources
///     type ExtractContext = ();
///
///     const EXTRACT_FIELDS: &'static [ExtractFieldInfo<Self>] = &[
///         field("my_extract.sample", &Self::extract_sample),
///     ];
/// }
///
/// plugin!(MyExtractPlugin);
/// extract_plugin!(MyExtractPlugin);
/// ```
///
/// See the [`extract::ExtractPlugin`] trait documentation for details.
pub mod extract {
    /// # An event from which additional data may be extracted
    ///
    /// The one notable thing about it is that it implements the [`EventInputExt`](`crate::EventInputExt`)
    /// trait. You probably won't need to access any of its fields directly.
    pub use falco_plugin_api::ss_plugin_event_input as EventInput;
    pub use falco_plugin_api::ss_plugin_field_extract_input as FieldExtractInput;

    pub use crate::plugin::extract::schema::field;
    pub use crate::plugin::extract::schema::{ExtractArgType, ExtractFieldInfo};
    pub use crate::plugin::extract::ExtractFieldRequestArg;
    pub use crate::plugin::extract::ExtractPlugin;
    pub use crate::plugin::storage::FieldStorage;
}

/// # Event parsing support
///
/// Plugins with event parsing capability can hook into an event stream and receive all of its events
/// sequentially. The parsing phase is the stage in the event processing loop in which
/// the Falcosecurity libraries inspect the content of the events' payload and use it to apply
/// internal state updates or implement additional logic. This phase happens before any field
/// extraction for a given event. Each event in a given stream is guaranteed to be received at most once.
///
/// For your plugin to support event parsing, you will need to implement the [`parse::ParsePlugin`]
/// trait and invoke the [`parse_plugin`] macro, for example:
///
/// ```
/// use std::ffi::{CStr, CString};
/// use std::sync::Arc;
/// use std::sync::atomic::{AtomicBool, Ordering};
/// use std::thread::JoinHandle;
/// use anyhow::Error;
/// use falco_event::{ };
/// use falco_event::events::types::EventType;
/// use falco_plugin::base::{InitInput, Plugin};
/// use falco_plugin::{EventInputExt, FailureReason, parse_plugin, plugin};
/// use falco_plugin::parse::{EventInput, ParseInput, ParsePlugin};
/// use falco_plugin_api::{ss_plugin_event_input, ss_plugin_event_parse_input};
///
/// struct MyParsePlugin;
///
/// impl Plugin for MyParsePlugin {
///     // ...
/// #    const NAME: &'static CStr = c"sample-plugin-rs";
/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
/// #    const CONTACT: &'static CStr = c"you@example.com";
/// #    type ConfigType = ();
/// #
/// #    fn new(input: &InitInput, config: Self::ConfigType)
/// #        -> Result<Self, FailureReason> {
/// #        Ok(MyParsePlugin)
/// #    }
/// #
/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
/// #        Ok(())
/// #    }
/// }
///
/// impl ParsePlugin for MyParsePlugin {
///     const EVENT_TYPES: &'static [EventType] = &[]; // inspect all events...
///     const EVENT_SOURCES: &'static [&'static str] = &[]; // ... from all event sources
///
///     fn parse_event(&mut self, event: &EventInput, parse_input: &ParseInput)
///         -> anyhow::Result<()> {
///         let event = event.event()?;
///         let event = event.load_any()?;
///
///         // any processing you want here, e.g. involving tables
///
///         Ok(())
///     }
/// }
///
/// plugin!(MyParsePlugin);
/// parse_plugin!(MyParsePlugin);
/// ```
pub mod parse {
    pub use falco_plugin_api::ss_plugin_event_input as EventInput;
    pub use falco_plugin_api::ss_plugin_event_parse_input as ParseInput;

    pub use crate::plugin::parse::EventParseInput;
    pub use crate::plugin::parse::ParsePlugin;
}

/// # Asynchronous event support
///
/// Plugins with async events capability can enrich an event stream from a given source (not
/// necessarily implemented by itself) by injecting events asynchronously in the stream. Such
/// a feature can be used for implementing notification systems or recording state transitions
/// in the event-driven model of the Falcosecurity libraries, so that they can be available to other
/// components at runtime or when the event stream is replayed through a capture file.
///
/// For example, the Falcosecurity libraries leverage this feature internally to implement metadata
/// enrichment systems such as the one related to container runtimes. In that case, the libraries
/// implement asynchronous jobs responsible for retrieving such information externally outside
/// the main event processing loop so that it's non-blocking. The worker jobs produce a notification
/// event every time a new container is detected and inject it asynchronously in the system event
/// stream to be later processed for state updates and for evaluating Falco rules.
///
/// For your plugin to support asynchronous events, you will need to implement the [`async_event::AsyncEventPlugin`]
/// trait and invoke the [`async_event`] macro, for example:
///
/// ```
/// use std::ffi::{CStr, CString};
/// use std::sync::Arc;
/// use std::sync::atomic::{AtomicBool, Ordering};
/// use std::thread::JoinHandle;
/// use anyhow::Error;
/// use falco_event::events::Event;
/// use falco_event::events::EventMetadata;
/// use falco_plugin::base::{InitInput, Plugin};
/// use falco_plugin::{async_event_plugin, EventInputExt, FailureReason, plugin};
/// use falco_plugin::async_event::{AsyncEvent, AsyncEventPlugin, AsyncHandler};
///
/// struct MyAsyncPlugin {
///     stop_request: Arc<AtomicBool>,
///     thread: Option<JoinHandle<Result<(), Error>>>,
/// }
///
/// impl Plugin for MyAsyncPlugin {
///     // ...
/// #    const NAME: &'static CStr = c"sample-plugin-rs";
/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
/// #    const CONTACT: &'static CStr = c"you@example.com";
/// #    type ConfigType = ();
/// #
/// #    fn new(input: &InitInput, config: Self::ConfigType)
/// #        -> Result<Self, FailureReason> {
/// #        Ok(MyAsyncPlugin {
/// #            stop_request: Arc::new(Default::default()),
/// #            thread: None,
/// #        })
/// #    }
/// #
/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
/// #        Ok(())
/// #    }
/// }
///
/// impl AsyncEventPlugin for MyAsyncPlugin {
///     const ASYNC_EVENTS: &'static [&'static str] = &[]; // generate any async events
///     const EVENT_SOURCES: &'static [&'static str] = &[]; // attach to all event sources
///
///     fn start_async(&mut self, handler: AsyncHandler) -> Result<(), Error> {
///         // stop the thread if it was already running
///         if self.thread.is_some() {
///            self.stop_async()?;
///         }
///
///         // start a new thread
///         self.stop_request.store(false, Ordering::Relaxed);
///         let stop_request = Arc::clone(&self.stop_request);
///         self.thread = Some(std::thread::spawn(move || {
///             // check the stop flag periodically: we must stop the thread
///             // when requested
///             while !stop_request.load(Ordering::Relaxed) {
///                 // build an event
///                 let event = AsyncEvent {
///                     plugin_id: Some(0),
///                     name: Some(c"sample_async"),
///                     data: Some(b"hello"),
///                 };
///
///                 let metadata = EventMetadata::default();
///
///                 let event = Event {
///                     metadata,
///                     params: event,
///                 };
///
///                 // submit it to the main event loop
///                 handler.emit(event)?;
///             }
///             Ok(())
///         }));
///         Ok(())
///     }
///
///     fn stop_async(&mut self) -> Result<(), Error> {
///         self.stop_request.store(true, Ordering::Relaxed);
///         let Some(handle) = self.thread.take() else {
///             return Ok(());
///         };
///
///         match handle.join() {
///             Ok(res) => res,
///             Err(e) => std::panic::resume_unwind(e),
///         }
///     }
/// }
///
/// plugin!(MyAsyncPlugin);
/// async_event_plugin!(MyAsyncPlugin);
/// ```
pub mod async_event {
    pub use falco_event::events::types::PPME_ASYNCEVENT_E as AsyncEvent;

    pub use crate::plugin::async_event::async_handler::AsyncHandler;
    pub use crate::plugin::async_event::AsyncEventPlugin;
}

/// # Event sourcing support
///
/// Plugins with event sourcing capability provide a new event source and make it available to
/// libscap and libsinsp. They have the ability to "open" and "close" a stream of events and return
/// those events to the plugin framework. They also provide a plugin ID, which is globally unique
/// and is used in capture files. Event sources provided by plugins with this capability are tied
/// to the events they generate and can be used by [plugins with field extraction](crate::source)
/// capabilities and within Falco rules.
/// For your plugin to support event sourcing, you will need to implement the [`source::SourcePlugin`]
/// trait and invoke the [`source_plugin`] macro, for example:
///
/// ```
/// use std::ffi::{CStr, CString};
/// use std::sync::Arc;
/// use std::sync::atomic::{AtomicBool, Ordering};
/// use std::thread::JoinHandle;
/// use anyhow::Error;
/// use falco_event::events::Event;
/// use falco_plugin::base::{InitInput, Plugin};
/// use falco_plugin::{EventInputExt, FailureReason, plugin, source_plugin};
/// use falco_plugin::source::{
///     EventBatch,
///     EventInput,
///     PluginEvent,
///     SourcePlugin,
///     SourcePluginInstance};
/// use falco_plugin_api::ss_plugin_event_input;
///
/// struct MySourcePlugin;
///
/// impl Plugin for MySourcePlugin {
///     // ...
/// #    const NAME: &'static CStr = c"sample-plugin-rs";
/// #    const PLUGIN_VERSION: &'static CStr = c"0.0.1";
/// #    const DESCRIPTION: &'static CStr = c"A sample Falco plugin that does nothing";
/// #    const CONTACT: &'static CStr = c"you@example.com";
/// #    type ConfigType = ();
/// #
/// #    fn new(input: &InitInput, config: Self::ConfigType)
/// #        -> Result<Self, FailureReason> {
/// #        Ok(MySourcePlugin)
/// #    }
/// #
/// #    fn set_config(&mut self, config: Self::ConfigType) -> Result<(), anyhow::Error> {
/// #        Ok(())
/// #    }
/// }
///
/// struct MySourcePluginInstance;
///
/// impl SourcePlugin for MySourcePlugin {
///     type Instance = MySourcePluginInstance;
///     const EVENT_SOURCE: &'static CStr = c"my-source-plugin";
///     const PLUGIN_ID: u32 = 0; // we do not have one assigned for this example :)
///
///     fn open(&mut self, params: Option<&str>) -> Result<Self::Instance, Error> {
///         // we do not use the open parameters in this example
///         Ok((MySourcePluginInstance))
///     }
///
///     fn event_to_string(&mut self, event: &EventInput) -> Result<CString, Error> {
///         // a string representation for our event; just copy out the whole event data
///         // (it's an ASCII string); please note we need the copy because we need to add
///         // a NUL terminator to convert the byte buffer to a C string
///
///         // get the raw event
///         let event = event.event()?;
///         // parse the fields into a PluginEvent
///         let plugin_event = event.load::<PluginEvent>()?;
///
///         // take a copy of the event data (it's in an Option because we never know if events
///         // have all the fields, and it's important to handle short events for backwards
///         // compatibility).
///         let data = plugin_event.params.event_data.map(|e| e.to_vec()).unwrap_or_default();
///
///         // convert the data to a CString and return it
///         Ok(CString::new(data)?)
///     }
/// }
///
/// impl SourcePluginInstance for MySourcePluginInstance {
///     type Plugin = MySourcePlugin;
///
///     fn next_batch(&mut self, plugin: &mut Self::Plugin, batch: &mut EventBatch)
///     -> Result<(), Error> {
///         let event = Self::plugin_event(b"hello, world");
///         batch.add(event)?;
///
///         Ok(())
///     }}
///
/// plugin!(MySourcePlugin);
/// source_plugin!(MySourcePlugin);
/// ```
pub mod source {
    pub use crate::plugin::source::event_batch::EventBatch;
    pub use crate::plugin::source::open_params::{serialize_open_params, OpenParam};
    pub use crate::plugin::source::{ProgressInfo, SourcePlugin, SourcePluginInstance};
    pub use crate::strings::cstring_writer::CStringWriter;
    pub use falco_event::events::types::PPME_PLUGINEVENT_E as PluginEvent;
    pub use falco_plugin_api::ss_plugin_event_input as EventInput;
}

/// # Creating and accessing tables
///
/// Tables are a mechanism to share data between plugins (and Falco core).
///
/// The main interface to this module is the [`base::TableInitInput`] trait.
pub mod tables {
    pub use crate::plugin::exported_tables::DynamicField;
    pub use crate::plugin::exported_tables::DynamicFieldValue;
    pub use crate::plugin::exported_tables::DynamicFieldValues;
    pub use crate::plugin::exported_tables::DynamicTable;
    pub use crate::plugin::exported_tables::ExportedTable;
    pub use crate::plugin::exported_tables::FieldValue;
    pub use crate::plugin::exported_tables::StaticField;
    pub use crate::plugin::exported_tables::TableValues;
    pub use crate::plugin::tables::data::Bool;
    pub use crate::plugin::tables::data::TableData;
    pub use crate::plugin::tables::data::TypedTableField;
    pub use crate::plugin::tables::entry::TableEntry;
    pub use crate::plugin::tables::entry::TableEntryReader;
    pub use crate::plugin::tables::table::TypedTable;
    pub use crate::plugin::tables::table_reader::TableReader;
    pub use falco_event::fields::TypeId;
}

mod plugin;
mod strings;

#[doc(hidden)]
pub mod internals {
    pub mod base {
        pub use crate::plugin::base::wrappers;
    }

    pub mod source {
        pub use crate::plugin::source::wrappers;
    }

    pub mod extract {
        pub use crate::plugin::extract::wrappers;
    }

    pub mod parse {
        pub use crate::plugin::parse::wrappers;
    }

    pub mod async_events {
        pub use crate::plugin::async_event::wrappers;
    }
}