regorus/compiled_policy.rs
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use crate::ast::*;
5use crate::engine::Engine;
6use crate::scheduler::*;
7use crate::utils::*;
8use crate::*;
9
10use alloc::collections::BTreeMap;
11use anyhow::Result;
12
13#[cfg(feature = "azure_policy")]
14use crate::target::Target;
15
16pub(crate) type DefaultRuleInfo = (Ref<Rule>, Option<crate::String>);
17
18#[cfg(feature = "azure_policy")]
19pub(crate) type ResourceTypeInfo = (Rc<str>, Rc<Schema>);
20
21#[cfg(feature = "azure_policy")]
22pub(crate) type InferredResourceTypes = BTreeMap<Ref<Query>, ResourceTypeInfo>;
23
24/// Wrapper around CompiledPolicyData that holds an Rc reference.
25#[derive(Debug, Clone)]
26pub struct CompiledPolicy {
27 inner: Rc<CompiledPolicyData>,
28}
29
30impl CompiledPolicy {
31 /// Create a new CompiledPolicy from CompiledPolicyData.
32 pub(crate) fn new(inner: Rc<CompiledPolicyData>) -> Self {
33 Self { inner }
34 }
35}
36
37impl CompiledPolicy {
38 /// Evaluate the compiled policy with the given input.
39 ///
40 /// For target policies, evaluates the target's effect rule.
41 /// For regular policies, evaluates the originally compiled rule.
42 ///
43 /// * `input`: Input data (resource) to validate against the policy.
44 ///
45 /// Returns the result of evaluating the rule.
46 pub fn eval_with_input(&self, input: Value) -> Result<Value> {
47 let mut engine = Engine::new_from_compiled_policy(self.inner.clone());
48
49 // Set input
50 engine.set_input(input);
51
52 // Evaluate the rule
53 #[cfg(feature = "azure_policy")]
54 if let Some(target_info) = self.inner.target_info.as_ref() {
55 return engine.eval_rule(target_info.effect_path.to_string());
56 }
57 engine.eval_rule(self.inner.rule_to_evaluate.to_string())
58 }
59
60 /// Get information about the compiled policy including metadata about modules,
61 /// target configuration, and resource types.
62 ///
63 /// Returns a [`crate::policy_info::PolicyInfo`] struct containing comprehensive
64 /// information about the compiled policy such as module IDs, target name,
65 /// applicable resource types, entry point rule, and parameters.
66 ///
67 /// # Examples
68 ///
69 /// ```no_run
70 /// use regorus::*;
71 /// # use std::sync::Arc;
72 ///
73 /// # fn main() -> anyhow::Result<()> {
74 /// # // Register a target for the example
75 /// # #[cfg(feature = "azure_policy")]
76 /// # {
77 /// # let target = regorus::target::Target::from_json_file("tests/interpreter/cases/target/definitions/sample_target.json")?;
78 /// # regorus::registry::targets::register(std::sync::Arc::new(target))?;
79 /// # }
80 ///
81 /// // Compile the policy
82 /// let policy_rego = r#"
83 /// package policy.example
84 /// import rego.v1
85 /// __target__ := "target.tests.sample_test_target"
86 ///
87 /// effect := "allow" if {
88 /// input.type == "storage_account"
89 /// input.location in ["eastus", "westus"]
90 /// }
91 /// "#;
92 ///
93 /// let modules = vec![regorus::PolicyModule {
94 /// id: "policy.rego".into(),
95 /// content: policy_rego.into(),
96 /// }];
97 ///
98 /// #[cfg(feature = "azure_policy")]
99 /// let compiled = regorus::compile_policy_for_target(Value::new_object(), &modules)?;
100 /// #[cfg(not(feature = "azure_policy"))]
101 /// let compiled = regorus::compile_policy_with_entrypoint(Value::new_object(), &modules, "allow".into())?;
102 /// let info = compiled.get_policy_info()?;
103 ///
104 /// assert_eq!(info.target_name, Some("target.tests.sample_test_target".into()));
105 /// assert_eq!(info.effect_rule, Some("effect".into()));
106 /// assert!(info.module_ids.len() > 0);
107 /// # Ok(())
108 /// # }
109 /// ```
110 pub fn get_policy_info(&self) -> Result<crate::policy_info::PolicyInfo> {
111 // Extract module IDs from the compiled policy
112 let module_ids: Vec<Rc<str>> = self
113 .inner
114 .modules
115 .iter()
116 .enumerate()
117 .map(|(i, module)| {
118 // Use source file path if available, otherwise generate an ID
119 let source_path = module.package.span.source.get_path();
120 if source_path.is_empty() {
121 format!("module_{}", i).into()
122 } else {
123 source_path.clone().into()
124 }
125 })
126 .collect();
127
128 // Extract target name and effect rule
129 #[cfg(feature = "azure_policy")]
130 let (target_name, effect_rule) = if let Some(target_info) = &self.inner.target_info {
131 (
132 Some(target_info.target.name.clone()),
133 Some(target_info.effect_name.clone()),
134 )
135 } else {
136 (None, None)
137 };
138
139 #[cfg(not(feature = "azure_policy"))]
140 let (target_name, effect_rule) = (None, None);
141
142 // Extract applicable resource types from inferred types
143 #[cfg(feature = "azure_policy")]
144 let applicable_resource_types: Vec<Rc<str>> =
145 if let Some(inferred_types) = &self.inner.inferred_resource_types {
146 inferred_types
147 .values()
148 .map(|(resource_type, _schema)| resource_type.clone())
149 .collect::<std::collections::BTreeSet<_>>() // Remove duplicates
150 .into_iter()
151 .collect()
152 } else {
153 Vec::new()
154 };
155
156 #[cfg(not(feature = "azure_policy"))]
157 let applicable_resource_types: Vec<Rc<str>> = Vec::new();
158
159 // Get parameters from the modules
160 #[cfg(feature = "azure_policy")]
161 let parameters = {
162 // Create a new engine from the compiled modules to extract parameters
163 let temp_engine = crate::engine::Engine::new_from_compiled_policy(self.inner.clone());
164
165 temp_engine.get_policy_parameters()?
166 };
167
168 Ok(crate::policy_info::PolicyInfo {
169 module_ids,
170 target_name,
171 applicable_resource_types,
172 entrypoint_rule: self.inner.rule_to_evaluate.clone(),
173 effect_rule,
174 #[cfg(feature = "azure_policy")]
175 parameters,
176 })
177 }
178}
179
180#[cfg(feature = "azure_policy")]
181#[derive(Debug, Clone)]
182pub(crate) struct TargetInfo {
183 pub(crate) target: Rc<Target>,
184 pub(crate) package: String,
185 pub(crate) effect_schema: Rc<Schema>,
186 pub(crate) effect_name: Rc<str>,
187 pub(crate) effect_path: Rc<str>,
188}
189
190#[derive(Debug, Clone, Default)]
191pub(crate) struct CompiledPolicyData {
192 pub(crate) modules: Rc<Vec<Ref<Module>>>,
193 pub(crate) schedule: Option<Schedule>,
194 pub(crate) rules: Map<String, Vec<Ref<Rule>>>,
195 pub(crate) default_rules: Map<String, Vec<DefaultRuleInfo>>,
196 pub(crate) imports: BTreeMap<String, Ref<Expr>>,
197 pub(crate) functions: FunctionTable,
198 pub(crate) rule_paths: Set<String>,
199 #[cfg(feature = "azure_policy")]
200 pub(crate) target_info: Option<TargetInfo>,
201 #[cfg(feature = "azure_policy")]
202 pub(crate) inferred_resource_types: Option<InferredResourceTypes>,
203
204 // User-defined rule to evaluate
205 pub(crate) rule_to_evaluate: Rc<str>,
206
207 // User-defined data
208 pub(crate) data: Option<Value>,
209
210 // Evaluation settings
211 pub(crate) strict_builtin_errors: bool,
212
213 // The semantics of extensions ought to be changes to be more Clone friendly.
214 pub(crate) extensions: Map<String, (u8, Rc<Box<dyn Extension>>)>,
215}