Skip to content

Commit

Permalink
feat: added support for running "mitre/activity" as a plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickjcasey authored and j-lanson committed Nov 4, 2024
1 parent b4dc782 commit 1195ea3
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 9 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions config/Hipcheck.kdl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
plugin "mitre/activity" version="0.1.0"
plugin "mitre/activity" version="0.1.0" manifest="./plugins/activity/plugin.kdl"
plugin "mitre/binary" version="0.1.0" manifest="./plugins/binary/plugin.kdl"
plugin "mitre/fuzz" version="0.1.0" manifest="./plugins/fuzz/plugin.kdl"
plugin "mitre/review" version="0.1.0" manifest="./plugins/review/plugin.kdl"
Expand All @@ -18,7 +18,7 @@ analyze {
investigate-if-fail "mitre/typo" "mitre/binary"

category "practices" {
analysis "mitre/activity" policy="(lte $ 52)" weight=3
analysis "mitre/activity" policy="(lte $ P52w)" weight=3
analysis "mitre/binary" {
binary-file "./config/Binary.toml"
binary-file-threshold 0
Expand Down
2 changes: 1 addition & 1 deletion hipcheck/src/plugin/retrieval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use xz2::read::XzDecoder;
use super::get_current_arch;

/// The plugins currently are not delegated via the `plugin` system and are still part of `hipcheck` core
pub const MITRE_LEGACY_PLUGINS: [&str; 2] = ["activity", "entropy"];
pub const MITRE_LEGACY_PLUGINS: [&str; 1] = ["entropy"];

/// determine all of the plugins that need to be run and locate download them, if they do not exist
pub fn retrieve_plugins(
Expand Down
10 changes: 5 additions & 5 deletions plugins/activity/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ static CONFIG: OnceLock<Config> = OnceLock::new();

/// Returns the span of time since the most recent commit to a Git repo as `jiff:Span` displayed as a String
/// (Which means that anything expecting a `Span` must parse the output of this query appropriately)
#[query]
async fn activity(engine: &mut PluginEngine, target: Target) -> Result<String> {
#[query(default)]
async fn activity(engine: &mut PluginEngine, target: Target) -> Result<AnnotatedJSONValue> {
log::debug!("running activity query");

let repo = target.local;
Expand Down Expand Up @@ -49,7 +49,7 @@ async fn activity(engine: &mut PluginEngine, target: Target) -> Result<String> {
Error::UnspecifiedQueryState
})?;

Ok(time_since_last_commit.to_string())
Ok(AnnotatedJSONValue::from_span(time_since_last_commit))
}

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -83,7 +83,7 @@ impl Plugin for ActivityPlugin {

fn explain_default_query(&self) -> Result<Option<String>> {
Ok(Some(
"Number of weeks since last activity in repo".to_string(),
"Span of time that has elapsed since last activity in repo".to_string(),
))
}

Expand Down Expand Up @@ -141,7 +141,7 @@ mod test {

let mut engine = PluginEngine::mock(mock_responses().unwrap());
let output = activity(&mut engine, target).await.unwrap();
let span: Span = output.parse().unwrap();
let span: Span = output.to_span().unwrap();
let result = span.round(SpanRound::new().smallest(Unit::Day)).unwrap();

let today = Timestamp::now();
Expand Down
2 changes: 1 addition & 1 deletion plugins/binary/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl TryFrom<RawConfig> for Config {
fn try_from(value: RawConfig) -> StdResult<Config, Self::Error> {
let Some(binary_file) = value.binary_file else {
return Err(ConfigError::MissingRequiredConfig {
field_name: "binary_file".to_owned(),
field_name: "binary-file".to_owned(),
field_type: "string".to_owned(),
possible_values: vec![],
});
Expand Down
1 change: 1 addition & 0 deletions sdk/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2021"
thiserror = "1.0.65"
futures = "0.3.31"
indexmap = "2.6.0"
jiff = { version = "0.1.13", features=["serde"]}
prost = "0.13.3"
rand = "0.8.5"
serde = { version = "1.0.214", features = ["derive"] }
Expand Down
2 changes: 2 additions & 0 deletions sdk/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub mod prelude {
pub use crate::error::{ConfigError, Error, Result};
pub use crate::plugin_engine::PluginEngine;
pub use crate::plugin_server::{PluginServer, QueryResult};
pub use crate::types::AnnotatedJSONValue;
pub use crate::{DynQuery, NamedQuery, Plugin, Query, QuerySchema, QueryTarget};
// Re-export macros
#[cfg(feature = "macros")]
Expand All @@ -40,6 +41,7 @@ pub mod prelude {

/// re-export of user-facing third-party dependencies
pub mod deps {
pub use jiff::{Span, Zoned};
pub use schemars::{schema::SchemaObject as JsonSchema, schema_for};
pub use serde_json::{from_str, from_value, to_value, Value};
pub use tonic::async_trait;
Expand Down
75 changes: 75 additions & 0 deletions sdk/rust/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// SPDX-License-Identifier: Apache-2.0

use std::{fmt::Debug, str::FromStr};

use jiff::{Span, Zoned};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use typify_macro::import_types;

import_types!(
Expand All @@ -12,3 +17,73 @@ import_types!(
} = url::Url,
}
);

/// Opaque type that holds data of a type that requires special type annotation
/// in JSON to parse correctly.
///
/// Currently, the following types are supported:
/// - `jiff::Zoned`
/// - `Jiff::Span`
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)]
pub struct AnnotatedJSONValue(AnnotatedJSONValueInner);

impl AnnotatedJSONValue {
/// Create a new AnnotatedJSONValue from a `jiff::Span`
pub fn from_span(span: jiff::Span) -> Self {
Self(AnnotatedJSONValueInner::Duration(span.to_string()))
}

/// Convert, if possible, the inner type to `jiff::Span`
pub fn to_span(&self) -> Option<Span> {
match &self.0 {
AnnotatedJSONValueInner::DateTime(_) => None,
// unwrap is safe because the only way to construct a DateTime variant is from a valid
// `jiff::Span`
AnnotatedJSONValueInner::Duration(span) => Some(Span::from_str(span).unwrap()),
}
}

/// Create a new AnnotatedJSONValue from a `jiff::Zoned`
pub fn from_datetime(duration: jiff::Zoned) -> Self {
Self(AnnotatedJSONValueInner::DateTime(duration.to_string()))
}

/// Convert, if possible, the inner type to `jiff::Zoned`
pub fn to_datetime(&self) -> Option<Zoned> {
match &self.0 {
// unwrap is safe because the only way to construct a DateTime variant is from a valid
// `jiff::Zoned`
AnnotatedJSONValueInner::DateTime(datetime) => Some(Zoned::from_str(datetime).unwrap()),
AnnotatedJSONValueInner::Duration(_) => None,
}
}
}

/// This type is purposely opaque to the user to prevent accessing inner fields of the enum and to
/// prevent creating invalid variants
///
/// The data will be a string called "data", and the format of the data will be
/// in "format".
/// Format names taken from JSON Schema.
/// Spec as of "Draft 2020-12":
/// https://json-schema.org/draft/2020-12/json-schema-validation#name-defined-formats
/// Prettier rendered docs of latest version:
/// https://json-schema.org/understanding-json-schema/reference/string#built-in-formats
#[derive(Debug, Deserialize, Serialize, JsonSchema)]
#[serde(tag = "format", content = "data")]
#[serde(rename_all = "lowercase")]
enum AnnotatedJSONValueInner {
/// Contains a string representing a jiff::Zoned
/// According to JSON Schema, "format" = "date-time" refers to a timestamp
/// encoded as specified by RFC 3339 (a subset of ISO 8601):
/// https://datatracker.ietf.org/doc/html/rfc3339#section-5.6
#[serde(rename = "date-time")]
DateTime(String),
/// Contains a string representing a jiff::Span
/// According to JSON Schema, "format" = "duration" refers to a time duration
/// encoded as specified by ISO 8601's ABNF:
/// https://datatracker.ietf.org/doc/html/rfc3339#appendix-A
#[serde(rename = "duration")]
Duration(String),
}

0 comments on commit 1195ea3

Please sign in to comment.