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 all 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,
}
}
68 changes: 67 additions & 1 deletion 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,26 @@ pub enum Error {
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 +226,41 @@ impl Engine {
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 @@ -290,7 +346,6 @@ impl Engine {

handle_errors(errors)?;

self.policy_package_count += added_policy_count;
Ok(added_policy_count)
}

Expand Down Expand Up @@ -558,4 +613,15 @@ mod tests {
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")?;
assert_eq!(1, engine.policy_package_count);

engine.add_policy_from_file_or_dir("data/multi-policies")?;
assert_eq!(3, engine.policy_package_count);
Ok(())
}
}
5 changes: 3 additions & 2 deletions src/registry/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ pub struct RegistryCheckArgs {
#[arg(long)]
baseline_registry: Option<RegistryPath>,

/// Optional list of policy files to check against the files of the semantic
/// convention registry.
/// Optional list of policy files or directories to check against the files of the semantic
/// convention registry. If a directory is provided all `.rego` files in the directory will be
/// loaded.
#[arg(short = 'p', long = "policy")]
pub policies: Vec<PathBuf>,

Expand Down
5 changes: 3 additions & 2 deletions src/registry/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ pub struct RegistryGenerateArgs {
#[command(flatten)]
registry: RegistryArgs,

/// Optional list of policy files to check against the files of the semantic
/// convention registry.
/// Optional list of policy files or directories to check against the files of the semantic
/// convention registry. If a directory is provided all `.rego` files in the directory will be
/// loaded.
#[arg(short = 'p', long = "policy")]
pub policies: Vec<PathBuf>,

Expand Down
5 changes: 3 additions & 2 deletions src/registry/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ pub struct RegistryResolveArgs {
#[arg(short, long, default_value = "yaml")]
format: Format,

/// Optional list of policy files to check against the files of the semantic
/// convention registry.
/// Optional list of policy files or directories to check against the files of the semantic
/// convention registry. If a directory is provided all `.rego` files in the directory will be
/// loaded.
#[arg(short = 'p', long = "policy")]
pub policies: Vec<PathBuf>,

Expand Down
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