Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let policy flag accept directories. #284

Merged
merged 5 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions crates/weaver_checker/data/multi-policies/otel_policies.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package before_resolution

# Conventions for OTel:
# - `data` holds the current released semconv, which is known to be valid.
# - `input` holds the new candidate semconv version, whose validity is unknown.
#
# Note: `data` and `input` are predefined variables in Rego.

# ========= Violation rules applied on unresolved semconv files =========

# A registry `attribute_group` containing at least one `ref` attribute is
# considered invalid.
deny[attr_registry_violation("registry_with_ref_attr", group.id, attr.ref)] {
group := input.groups[_]
startswith(group.id, "registry.")
attr := group.attributes[_]
attr.ref != null
}

# An attribute whose stability is not `deprecated` but has the deprecated field
# set to true is invalid.
deny[attr_violation("attr_stability_deprecated", group.id, attr.id)] {
group := input.groups[_]
attr := group.attributes[_]
attr.stability != "deprecaded"
attr.deprecated
}

# An attribute cannot be removed from a group that has already been released.
deny[schema_evolution_violation("attr_removed", old_group.id, old_attr.id)] {
old_group := data.groups[_]
old_attr := old_group.attributes[_]
not attr_exists_in_new_group(old_group.id, old_attr.id)
}


# ========= Helper functions =========

# Check if an attribute from the old group exists in the new
# group's attributes
attr_exists_in_new_group(group_id, attr_id) {
new_group := input.groups[_]
new_group.id == group_id
attr := new_group.attributes[_]
attr.id == attr_id
}

# Build an attribute registry violation
attr_registry_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "attrigute_registry",
"group": group_id,
"attr": attr_id,
}
}

# Build an attribute violation
attr_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "attrigute",
"group": group_id,
"attr": attr_id,
}
}

# Build a schema evolution violation
schema_evolution_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "schema_evolution",
"group": group_id,
"attr": attr_id,
}
}
79 changes: 79 additions & 0 deletions crates/weaver_checker/data/multi-policies/otel_policies_2.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package before_resolution

# Conventions for OTel:
# - `data` holds the current released semconv, which is known to be valid.
# - `input` holds the new candidate semconv version, whose validity is unknown.
#
# Note: `data` and `input` are predefined variables in Rego.

# ========= Violation rules applied on unresolved semconv files =========

# A registry `attribute_group` containing at least one `ref` attribute is
# considered invalid.
deny[attr_registry_violation("registry_with_ref_attr", group.id, attr.ref)] {
group := input.groups[_]
startswith(group.id, "registry.")
attr := group.attributes[_]
attr.ref != null
}

# An attribute whose stability is not `deprecated` but has the deprecated field
# set to true is invalid.
deny[attr_violation("attr_stability_deprecated", group.id, attr.id)] {
group := input.groups[_]
attr := group.attributes[_]
attr.stability != "deprecaded"
attr.deprecated
}

# An attribute cannot be removed from a group that has already been released.
deny[schema_evolution_violation("attr_removed", old_group.id, old_attr.id)] {
old_group := data.groups[_]
old_attr := old_group.attributes[_]
not attr_exists_in_new_group(old_group.id, old_attr.id)
}


# ========= Helper functions =========

# Check if an attribute from the old group exists in the new
# group's attributes
attr_exists_in_new_group(group_id, attr_id) {
new_group := input.groups[_]
new_group.id == group_id
attr := new_group.attributes[_]
attr.id == attr_id
}

# Build an attribute registry violation
attr_registry_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "attrigute_registry",
"group": group_id,
"attr": attr_id,
}
}

# Build an attribute violation
attr_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "attrigute",
"group": group_id,
"attr": attr_id,
}
}

# Build a schema evolution violation
schema_evolution_violation(violation_id, group_id, attr_id) = violation {
violation := {
"id": violation_id,
"type": "semconv_attribute",
"category": "schema_evolution",
"group": group_id,
"attr": attr_id,
}
}
66 changes: 66 additions & 0 deletions crates/weaver_checker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

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

use globset::Glob;
Expand Down Expand Up @@ -43,6 +44,24 @@
error: String,
},

/// An unsupported policy path.
#[error("Invalid policy path '{path}'")]
#[diagnostic(help("The specified path is neither a file nor a directory.”"))]
UnsupportedPolicyPath {
/// The path that caused the error.
path: String,
},

/// Unable to access policy path.
#[error("Invalid policy path '{path}'")]
#[diagnostic(help("Verify that the specified path exists and has the appropriate permissions."))]
AccessDenied {
/// The path that caused the error.
path: String,
/// The error that occurred.
error: String,
},

/// An invalid policy glob pattern.
#[error("Invalid policy glob pattern '{pattern}', error: {error})")]
#[diagnostic(
Expand Down Expand Up @@ -205,6 +224,41 @@
Ok(policy_package)
}

/// Adds a policy files to the policy engine. If path is a directory it will add any file matching *.rego
/// A policy file is a `rego` file that contains the policies to be evaluated.
///
/// # Arguments
///
/// * `policy_path` - The path to the policy file or directory.
///
/// # Returns
///
/// The policy package name.
pub fn add_policy_from_file_or_dir<P: AsRef<Path>>(
&mut self,
policy_path: P,
) -> Result<(), Error> {
let path = policy_path.as_ref();

let md = metadata(path).map_err(|err| Error::AccessDenied {
path: path.to_string_lossy().to_string(),
error: err.to_string(),
})?;
match (md.is_file(), md.is_dir()) {
(true, _) => {
_ = self.add_policy_from_file(path)?;
}
(false, true) => {
_ = self.add_policies(path, "*.rego")?;
}
_ => {
return Err(Error::UnsupportedPolicyPath {
path: path.to_string_lossy().to_string(),
});
}
};
Ok(())
}
/// Adds a policy file to the policy engine.
/// A policy file is a `rego` file that contains the policies to be evaluated.
///
Expand Down Expand Up @@ -558,4 +612,16 @@
panic!("Expected a CompoundError");
}
}

#[test]
fn test_policy_from_file_or_dir() -> Result<(), Box<dyn std::error::Error>> {
let mut engine = Engine::new();
_ = engine.add_policy_from_file_or_dir("data/policies/otel_policies.rego")?;
Fixed Show fixed Hide fixed
assert_eq!(1, engine.policy_package_count);

_ = engine.add_policy_from_file_or_dir("data/multi-policies")?;
Fixed Show fixed Hide fixed
// TODO: add_policies double counts the number of files it adds.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, there is double counting in add_policies. It would be good to take advantage of this PR to fix the bug. Thank you.

assert_eq!(5, engine.policy_package_count);
Ok(())
}
}
3 changes: 1 addition & 2 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,8 @@ pub(crate) fn init_policy_engine(

// Add policies from the command line
for policy in policies {
_ = engine.add_policy_from_file(policy)?;
engine.add_policy_from_file_or_dir(policy)?;
}

Ok(engine)
}

Expand Down
Loading