#![forbid(unsafe_code)]
#![deny(unknown_lints)]
#![deny(
// Panic sources - catch all ways code can panic
clippy::panic, // forbid explicit panic! macro
clippy::unreachable, // catches unreachable! macro usage
clippy::todo, // blocks remaining todo! placeholders
clippy::unimplemented, // blocks unimplemented! placeholders
clippy::unwrap_used, // reject Result/Option unwraps
clippy::expect_used, // reject expect with panic messages
clippy::manual_assert, // prefer assert! over manual if/panic
clippy::indexing_slicing, // reject unchecked [] indexing
clippy::arithmetic_side_effects, // reject overflowing/unchecked math
clippy::panic_in_result_fn, // disallow panic inside functions returning Result
// Rust warnings/upstream
dead_code, // ban unused items
deprecated, // prevent use of deprecated APIs
deprecated_in_future, // catch items scheduled for deprecation
exported_private_dependencies, // avoid leaking private deps in public API
future_incompatible, // catch patterns slated to break
invalid_doc_attributes, // ensure doc attributes are valid
keyword_idents, // disallow identifiers that are keywords
macro_use_extern_crate, // block legacy macro_use extern crate
missing_debug_implementations, // require Debug on public types
// TODO: Address in future pass
// missing_docs, // require docs on public items
non_ascii_idents, // disallow non-ASCII identifiers
nonstandard_style, // enforce idiomatic naming/style
noop_method_call, // catch no-op method calls
trivial_bounds, // forbid useless trait bounds
trivial_casts, // block needless casts
unreachable_code, // catch dead/unreachable code
unreachable_patterns, // catch unreachable match arms
// TODO: Address in future pass
// unreachable_pub,
unused_extern_crates, // remove unused extern crate declarations
unused_import_braces, // avoid unused braces in imports
absolute_paths_not_starting_with_crate, // enforce crate:: prefix for absolute paths
// Unsafe code / low-level hazards
clippy::unseparated_literal_suffix, // enforce underscore before literal suffixes
clippy::print_stderr, // discourage printing to stderr
clippy::use_debug, // discourage Debug formatting in display contexts
// Documentation & diagnostics
// TODO: Address in future pass
// clippy::doc_link_with_quotes, // avoid quoted intra-doc links
// clippy::doc_markdown, // flag bad Markdown in docs
// clippy::missing_docs_in_private_items, // require docs on private items
// clippy::missing_errors_doc, // require docs for error cases
// API correctness / style
clippy::missing_const_for_fn, // suggest const fn where possible
clippy::option_if_let_else, // prefer map_or/unwrap_or_else over if/let
clippy::if_then_some_else_none, // prefer Option combinators over if/else
clippy::semicolon_if_nothing_returned, // enforce trailing semicolon for unit
clippy::unused_self, // remove unused self parameters
clippy::used_underscore_binding, // avoid using bindings prefixed with _
clippy::useless_let_if_seq, // simplify let-if sequences
clippy::similar_names, // flag confusingly similar identifiers
clippy::shadow_unrelated, // discourage shadowing unrelated variables
clippy::redundant_pub_crate, // avoid pub(crate) on already pub items
clippy::wildcard_dependencies, // disallow wildcard Cargo dependency versions
// TODO: Address in future pass
// clippy::wildcard_imports, // discourage glob imports
// Numeric correctness
// TODO: Address in future pass
clippy::float_cmp, // avoid exact float equality checks
clippy::float_cmp_const, // avoid comparing floats to consts directly
clippy::float_equality_without_abs, // require tolerance in float equality
clippy::suspicious_operation_groupings, // catch ambiguous operator precedence
// no_std hygiene
clippy::std_instead_of_core, // prefer core/alloc over std in no_std
// Misc polish
clippy::dbg_macro, // forbid dbg! in production code
clippy::debug_assert_with_mut_call, // avoid mutating inside debug_assert
clippy::empty_line_after_outer_attr, // enforce spacing after outer attrs
clippy::empty_structs_with_brackets, // use unit structs without braces
)]
#![warn(
clippy::assertions_on_result_states, // avoid asserts on Result state
clippy::match_like_matches_macro, // prefer matches! macro over verbose match
clippy::needless_continue, // remove redundant continue statements
clippy::unused_trait_names, // drop unused trait imports
clippy::verbose_file_reads, // prefer concise file read helpers
clippy::as_conversions, // discourage lossy as casts
clippy::pattern_type_mismatch, // catch mismatched types in patterns
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))]
#![no_std]
extern crate alloc;
use serde::Serialize;
#[cfg(any(feature = "std", test))]
extern crate std;
#[cfg(feature = "mimalloc")]
mimalloc::assign_global!();
mod ast;
mod builtins;
mod compile;
mod compiled_policy;
mod compiler;
mod engine;
mod indexchecker;
mod interpreter;
pub mod languages {
#[cfg(feature = "azure-rbac")]
pub mod azure_rbac;
#[cfg(feature = "rvm")]
pub mod rego;
}
mod lexer;
pub(crate) mod lookup;
mod number;
mod parser;
mod policy_info;
mod query;
#[cfg(feature = "azure_policy")]
pub mod registry;
#[cfg(feature = "rvm")]
pub mod rvm;
mod scheduler;
#[cfg(feature = "azure_policy")]
mod schema;
#[cfg(feature = "azure_policy")]
pub mod target;
#[cfg(any(test, all(feature = "yaml", feature = "std")))]
pub mod test_utils;
pub mod utils;
mod value;
#[cfg(feature = "azure_policy")]
pub use {
compile::compile_policy_for_target,
schema::{error::ValidationError, validate::SchemaValidator, Schema},
target::Target,
};
pub use compile::{compile_policy_with_entrypoint, PolicyModule};
pub use compiled_policy::CompiledPolicy;
pub use engine::Engine;
pub use lexer::Source;
pub use policy_info::PolicyInfo;
pub use utils::limits::LimitError;
#[cfg(feature = "allocator-memory-limits")]
pub use utils::limits::{
check_global_memory_limit, enforce_memory_limit, flush_thread_memory_counters,
global_memory_limit, set_global_memory_limit, set_thread_flush_threshold_override,
thread_memory_flush_threshold,
};
pub use value::Value;
#[cfg(feature = "arc")]
pub use alloc::sync::Arc as Rc;
#[cfg(not(feature = "arc"))]
pub use alloc::rc::Rc;
#[cfg(feature = "std")]
use std::collections::{hash_map::Entry as MapEntry, HashMap as Map, HashSet as Set};
#[cfg(not(feature = "std"))]
use alloc::collections::{btree_map::Entry as MapEntry, BTreeMap as Map, BTreeSet as Set};
use alloc::{
borrow::ToOwned as _,
boxed::Box,
format,
string::{String, ToString as _},
vec,
vec::Vec,
};
use core::fmt;
#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct Location {
pub row: u32,
pub col: u32,
}
#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct Expression {
pub value: Value,
pub text: Rc<str>,
pub location: Location,
}
#[derive(Debug, Clone, Serialize, Eq, PartialEq)]
pub struct QueryResult {
pub expressions: Vec<Expression>,
#[serde(skip_serializing_if = "Value::is_empty_object")]
pub bindings: Value,
}
impl Default for QueryResult {
fn default() -> Self {
Self {
bindings: Value::new_object(),
expressions: vec![],
}
}
}
#[derive(Debug, Clone, Default, Serialize, Eq, PartialEq)]
pub struct QueryResults {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub result: Vec<QueryResult>,
}
pub trait Extension: FnMut(Vec<Value>) -> anyhow::Result<Value> + Send + Sync {
fn clone_box<'a>(&self) -> Box<dyn 'a + Extension>
where
Self: 'a;
}
impl<F> Extension for F
where
F: FnMut(Vec<Value>) -> anyhow::Result<Value> + Clone + Send + Sync,
{
fn clone_box<'a>(&self) -> Box<dyn 'a + Extension>
where
Self: 'a,
{
Box::new(self.clone())
}
}
impl Clone for Box<dyn '_ + Extension> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
impl fmt::Debug for dyn Extension {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::result::Result<(), fmt::Error> {
f.write_fmt(format_args!("<extension>"))
}
}
#[cfg(feature = "coverage")]
#[cfg_attr(docsrs, doc(cfg(feature = "coverage")))]
pub mod coverage {
use crate::*;
#[allow(missing_debug_implementations)]
#[derive(Default, serde::Serialize, serde::Deserialize)]
pub struct File {
pub path: String,
pub code: String,
pub covered: alloc::collections::BTreeSet<u32>,
pub not_covered: alloc::collections::BTreeSet<u32>,
}
#[allow(missing_debug_implementations)]
#[derive(Default, serde::Serialize, serde::Deserialize)]
pub struct Report {
pub files: Vec<File>,
}
impl Report {
#[allow(clippy::arithmetic_side_effects)]
pub fn to_string_pretty(&self) -> anyhow::Result<String> {
let mut s = String::default();
s.push_str("COVERAGE REPORT:\n");
for file in self.files.iter() {
if file.not_covered.is_empty() {
s.push_str(&format!("{} has full coverage\n", file.path));
continue;
}
s.push_str(&format!("{}:\n", file.path));
for (line_idx, code) in file.code.split('\n').enumerate() {
let line = u32::try_from(line_idx + 1).unwrap_or(u32::MAX);
if file.not_covered.contains(&line) {
s.push_str(&format!("\x1b[31m {line:4} {code}\x1b[0m\n"));
} else if file.covered.contains(&line) {
s.push_str(&format!("\x1b[32m {line:4} {code}\x1b[0m\n"));
} else {
s.push_str(&format!(" {line:4} {code}\n"));
}
}
}
s.push('\n');
Ok(s)
}
}
}
#[doc(hidden)]
pub mod unstable {
pub use crate::ast::*;
pub use crate::lexer::*;
pub use crate::parser::*;
}
#[cfg(test)]
mod tests;