Skip to content

Commit

Permalink
feat(checker): Add support for after_resolution policies.
Browse files Browse the repository at this point in the history
  • Loading branch information
lquerel committed Jun 18, 2024
1 parent f24f85c commit e3b5c19
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 120 deletions.
33 changes: 21 additions & 12 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion crates/weaver_checker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ walkdir.workspace = true
globset.workspace = true
miette.workspace = true

regorus = { version = "0.1.5", default-features = false, features = [
regorus = { version = "0.2.0", default-features = false, features = [
"std",
"arc",
"base64",
"base64url",
Expand Down
61 changes: 48 additions & 13 deletions crates/weaver_checker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#![allow(rustdoc::broken_intra_doc_links)]
#![doc = include_str!("../README.md")]

use std::collections::HashSet;
use std::fmt::{Display, Formatter};
use std::path::Path;

Expand All @@ -11,12 +12,12 @@ use miette::Diagnostic;
use serde::Serialize;
use serde_json::to_value;
use walkdir::DirEntry;
use weaver_common::diagnostic::{DiagnosticMessage, DiagnosticMessages};

use weaver_common::diagnostic::{DiagnosticMessage, DiagnosticMessages};
use weaver_common::error::{format_errors, handle_errors, WeaverError};

use crate::violation::Violation;
use crate::Error::CompoundError;
use crate::violation::Violation;

pub mod violation;

Expand Down Expand Up @@ -146,8 +147,13 @@ impl Display for PolicyStage {
pub struct Engine {
// The `regorus` policy engine.
engine: regorus::Engine,
// Flag to enable the coverage report.
coverage_enabled: bool,
// Number of policy packages added.
policy_package_count: usize,
// Policy packages loaded. This is used to check if a policy package has been imported
// before evaluating it.
policy_packages: HashSet<String>,
}

impl Engine {
Expand All @@ -157,23 +163,34 @@ impl Engine {
Default::default()
}

/// Enables the coverage report.
pub fn enable_coverage(&mut self) {
self.engine.set_enable_coverage(true);
self.coverage_enabled = true;
}

/// Adds a policy file to the policy engine.
/// A policy file is a `rego` file that contains the policies to be evaluated.
///
/// # Arguments
///
/// * `policy_path` - The path to the policy file.
pub fn add_policy<P: AsRef<Path>>(&mut self, policy_path: P) -> Result<(), Error> {
pub fn add_policy<P: AsRef<Path>>(&mut self, policy_path: P) -> Result<String, Error> {
let policy_path_str = policy_path.as_ref().to_string_lossy().to_string();

self.engine
let policy_package = self.engine
.add_policy_from_file(policy_path)
.map_err(|e| Error::InvalidPolicyFile {
file: policy_path_str.clone(),
error: e.to_string(),
}).inspect(|_| {
self.policy_package_count += 1;
})
})?;
// Add the policy package defined in the imported policy file.
// Nothing prevent multiple policy files to import the same policy package.
// All the rules will be combined and evaluated together.
_ = self.policy_packages.insert(policy_package.clone());
Ok(policy_package)
}

/// Adds all the policy files present in the given directory that match the
Expand Down Expand Up @@ -291,12 +308,30 @@ impl Engine {
/// Returns a list of violations based on the policies, the data, the
/// input, and the given policy stage.
pub fn check(&mut self, stage: PolicyStage) -> Result<Vec<Violation>, Error> {
// If we don't have any policy package that matches the stage,
// return an empty list of violations.
if !self.policy_packages.contains(&format!("data.{}", stage)) {
return Ok(vec![]);
}

let value = self
.engine
.eval_rule(format!("data.{}.deny", stage))
.map_err(|e| Error::ViolationEvaluationError {
error: e.to_string(),
error: e.to_string()
})?;

// Print the coverage report if enabled
// This is useful for debugging purposes
if self.coverage_enabled {
let report = self.engine.get_coverage_report().map_err(|e| Error::ViolationEvaluationError {
error: e.to_string()
})?;
let pretty_report = report.to_string_pretty().map_err(|e| Error::ViolationEvaluationError {
error: e.to_string()
})?;
println!("{}", pretty_report);
}

// convert `regorus` value to `serde_json` value
let json_value = to_value(&value).map_err(|e| Error::ViolationEvaluationError {
Expand All @@ -321,8 +356,8 @@ mod tests {

use weaver_common::error::format_errors;

use crate::violation::Violation;
use crate::{Engine, Error, PolicyStage};
use crate::violation::Violation;

#[test]
fn test_policy() -> Result<(), Box<dyn std::error::Error>> {
Expand Down Expand Up @@ -357,9 +392,9 @@ mod tests {
attr: "protocol.port".to_owned(),
},
]
.into_iter()
.map(|v| (v.id().to_owned(), v))
.collect();
.into_iter()
.map(|v| (v.id().to_owned(), v))
.collect();

let violations = engine.check(PolicyStage::BeforeResolution)?;
assert_eq!(violations.len(), 3);
Expand Down Expand Up @@ -442,9 +477,9 @@ mod tests {
attr: "protocol.port".to_owned(),
},
]
.into_iter()
.map(|v| (v.id().to_owned(), v))
.collect();
.into_iter()
.map(|v| (v.id().to_owned(), v))
.collect();

let violations = engine.check(PolicyStage::BeforeResolution)?;
assert_eq!(violations.len(), 3);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package after_resolution

# Example of rules that will be applied on resolved semconv files

# Detect `http.request.method` attribute and consider it invalid.
# This is just an example for testing purposes.
deny[invalid_attr_violation("invalid_http_attr", group.id, attr.name)] {
group := input.groups[_]
attr := group.attributes[_]
attr.name == "http.request.method"
}

invalid_attr_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "attrigute",
"group": group_id,
"attr": attr_id,
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package after_resolution

# Example of rules that will be applied on resolved semconv files

# Detect `system.cpu.logical_number` attribute and consider it invalid.
# This is just an example for testing purposes.
deny[invalid_attr_violation("invalid_metric_attr", group.id, attr.name)] {
group := input.groups[_]
attr := group.attributes[_]
attr.name == "system.cpu.logical_number"
}

invalid_attr_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "attrigute",
"group": group_id,
"attr": attr_id,
}
}
79 changes: 0 additions & 79 deletions crates/weaver_codegen_test/semconv_registry/post-resolution.rego

This file was deleted.

Loading

0 comments on commit e3b5c19

Please sign in to comment.