polarlabs / osquery-rust

Rust bindings for osquery
Apache License 2.0
6 stars 2 forks source link

derive columns from struct #4

Open jcgruenhage opened 1 year ago

jcgruenhage commented 1 year ago

Is your feature request related to a problem? Please describe. If each column I'm returning is defined by a struct, it's very frustrating to replicate the fields into both ColumnDefs and into ExtensionPluginResponse entries by hand.

Describe the solution you'd like Define a trait and implement a derive macro, so that ColumnDefs are automatically generated for each field in a struct, and that allow turning a value of that struct into a response entry.

Describe alternatives you've considered Manually implementing that stuff, which is what I'm doing right now, but that's super annoying.

Additional context Add any other context or screenshots about the feature request here.

fn xbps_packages_columns() -> Vec<ColumnDef> {
    vec![
        ColumnDef::new("name", ColumnType::Text),
        ColumnDef::new("version", ColumnType::Text),
        ColumnDef::new("description", ColumnType::Text),
        ColumnDef::new("homepage", ColumnType::Text),
        ColumnDef::new("maintainer", ColumnType::Text),
        ColumnDef::new("install_date", ColumnType::Text),
        ColumnDef::new("license", ColumnType::Text),
        ColumnDef::new("repository", ColumnType::Text),
    ]
}

fn xbps_packages_generate(_req: ExtensionPluginRequest) -> ExtensionResponse {
    let mut resp = ExtensionPluginResponse::new();

    let pkgdb : Pkgdb = plist::from_file("/var/db/xbps/pkgdb-0.38.plist").expect("failed to read pkgdb");

    for (_name, pkg) in pkgdb {
        let mut pkgmap = BTreeMap::new();

        pkgmap.insert("name".to_string(), pkg.pkgname);
        pkgmap.insert("version".to_string(), pkg.pkgver);
        pkgmap.insert("description".to_string(), pkg.short_desc);
        pkgmap.insert("homepage".to_string(), pkg.homepage);
        pkgmap.insert("maintainer".to_string(), pkg.maintainer);
        pkgmap.insert("install_date".to_string(), pkg.install_date);
        pkgmap.insert("license".to_string(), pkg.license);
        pkgmap.insert("repository".to_string(), pkg.repository);

        resp.push(pkgmap);
    }

    ExtensionResponse::new(ExtensionStatus::default(), resp)
}
jcgruenhage commented 1 year ago

I've thought a bit more about this, and it's a bit more complicated than I initially thought. A struct can be the source of multiple tables: In the example above, a package might have multiple dependencies, meaning that there would be a table for the package that includes every field in the struct that can only have a single value, and an additional table for ever field that can contain multiple values. These additional tables need to have some identifier from the main table, so defining a key that's copied to all additional tables is also required. All in all, not terribly difficult, but doing the right thing with nested structs will be a bit more difficult. Maybe this is something where we can piggy-back on the attributes provided by serde as well, so that we don't have to replicate renames across multiple places for example.

tokcum commented 2 months ago

I've to recap the approach I took with osquery-rust. I'm sure it is quite basic at the moment because back at the time rust was pretty new to me.

Let me know, if you are still interested in getting the package and dependency list done with your own osquery plugin implemented in rust. Thank you.

tokcum commented 2 months ago

@jcgruenhage, I looked into this and learned a lot. I've never heard of voidlinux or xbps before. I did not find osquery in the official voidlinux repos. I've also found https://git.jcg.re/jcgruenhage/osquery-voidlinux. I appreciate it.

Where do you get osquery for voidlinux from?

jcgruenhage commented 2 months ago

I used the binary releases from upstream.

I'm not using Void Linux anymore, I have since migrated to Cgimera Linux

tokcum commented 2 months ago

:) You make me learn about other Linux distributions.

tokcum commented 2 months ago

ChimeraLinux uses APK (Alpine Package Keeper) for package management. As far as I can see, this is also not (yet) supported by Osquery.

If it would be supported by Osquery, the data would be provided in table(s) dedicated to APK. This approach makes it hard to ask for an installed base in a heterogeneous Linux environment with a single SQL statement. I'm facing this challenge with RPM based and APT based distributions today. I solved it but the solution is not simple and with additional package managers on stage it gets worse.

So, I think, it would be nice to have one extension which covers "all" package managers with a common interface within Osquery.