Lock down ACI tests and more OPA test folders (#54)

Borrowed from rego-cpp

Significantly (> 10 times) faster execution.

$ cargo test -r  --test aci
aci/mount_device                                  passed    9.597958ms
aci/mount_overlay                                 passed    10.159208ms
aci/scratch_mount                                 passed    8.598875ms
aci/create_container                              passed    10.237292ms
aci/shutdown_container                            passed    6.904084ms
aci/scratch_unmount                               passed    6.530875ms
aci/unmount_overlay                               passed    5.958875ms
aci/unmount_device                                passed    5.657834ms
aci/load_fragment                                 passed    6.049917ms

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2023-12-01 08:32:11 -08:00
committed by GitHub
parent 639ba72c90
commit bb0ca29753
14 changed files with 2712 additions and 103 deletions

View File

@@ -17,6 +17,7 @@ env_logger="0.10.0"
lazy_static = "1.4.0"
rand = "0.8.5"
data-encoding = "2.4.0"
regex = "1.10.2"
[dev-dependencies]
clap = { version = "4.4.7", features = ["derive"] }
@@ -33,3 +34,8 @@ debug = true
name="opa"
harness=false
test=false
[[test]]
name="aci"
harness=false
test=false

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT License.
use crate::ast::{Expr, Ref};
use crate::builtins::regex::regex_match;
use crate::builtins::utils::{ensure_args_count, ensure_set};
use crate::builtins::BuiltinFcn;
use crate::lexer::Span;
@@ -20,6 +21,7 @@ lazy_static! {
m.insert("all", (all, 1));
m.insert("any", (any, 1));
m.insert("set_diff", (set_diff, 2));
m.insert("re_match", (regex_match, 2));
m
};
}

View File

@@ -11,6 +11,7 @@ pub mod deprecated;
mod encoding;
pub mod numbers;
mod objects;
mod regex;
mod semver;
pub mod sets;
mod strings;
@@ -45,7 +46,7 @@ lazy_static! {
sets::register(&mut m);
objects::register(&mut m);
strings::register(&mut m);
//regex::register(&mut m);
regex::register(&mut m);
//glob::register(&mut m);
bitwise::register(&mut m);
conversions::register(&mut m);

51
src/builtins/regex.rs Normal file
View File

@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use crate::ast::{Expr, Ref};
use crate::builtins;
use crate::builtins::utils::{ensure_args_count, ensure_string};
use crate::lexer::Span;
use crate::value::Value;
use std::collections::HashMap;
use anyhow::{bail, Result};
use regex::Regex;
pub fn register(m: &mut HashMap<&'static str, builtins::BuiltinFcn>) {
m.insert("regex.is_valid", (is_valid, 1));
m.insert("regex.match", (regex_match, 2));
m.insert("regex.split", (regex_split, 2));
}
fn is_valid(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "regex.is_valid";
ensure_args_count(span, name, params, args, 1)?;
Ok(ensure_string(name, &params[0], &args[0])
.map_or(Value::Bool(false), |p| Value::Bool(Regex::new(&p).is_ok())))
}
pub fn regex_match(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "regex.match";
ensure_args_count(span, name, params, args, 2)?;
let pattern = ensure_string(name, &params[0], &args[0])?;
let value = ensure_string(name, &params[1], &args[1])?;
let pattern = Regex::new(&pattern).or_else(|_| bail!(span.error("invalid regex")))?;
Ok(Value::Bool(pattern.is_match(&value)))
}
pub fn regex_split(span: &Span, params: &[Ref<Expr>], args: &[Value]) -> Result<Value> {
let name = "regex.split";
ensure_args_count(span, name, params, args, 2)?;
let pattern = ensure_string(name, &params[0], &args[0])?;
let value = ensure_string(name, &params[1], &args[1])?;
let pattern = Regex::new(&pattern).or_else(|_| bail!(span.error("invalid regex")))?;
Ok(Value::from_array(
pattern
.split(&value)
.map(|s| Value::String(s.into()))
.collect::<Vec<Value>>(),
))
}

View File

@@ -242,7 +242,7 @@ impl Interpreter {
// Stop path collection upon encountering the leading variable.
Expr::Var(v) => {
path.reverse();
return self.lookup_var(v, &path[..]);
return self.lookup_var(v, &path[..], false);
}
// Accumulate chained . field accesses.
Expr::RefDot { refr, field, .. } => {
@@ -259,8 +259,23 @@ impl Interpreter {
// Note, we have the choice to evaluate a non-string index
_ => {
path.reverse();
let obj = self.eval_expr(refr)?;
let index = self.eval_expr(index)?;
// Handle indexing into data.
if let Ok(ref_path) = get_path_string(refr, None) {
if get_root_var(refr)?.text() == "data" && index != Value::Undefined {
let index = match &index {
Value::String(s) => s.to_string(),
_ => index.to_string(),
};
let ref_path = ref_path + "." + &index + "." + &path.join("");
self.ensure_rule_evaluated(ref_path)?;
}
}
let obj = self.eval_expr(refr)?;
let mut v = obj[&index].clone();
// Qualified references starting with data (e.g data.p.q) can
// be indexed using numbers. The number will be converted to string
@@ -451,55 +466,87 @@ impl Interpreter {
let (name, value) = match op {
AssignOp::Eq => {
match (lhs.as_ref(), rhs.as_ref()) {
(Expr::Var(lhs_span), Expr::Var(rhs_span)) => {
let (lhs_name, lhs_var) = (lhs_span.source_str(), self.eval_expr(lhs)?);
let (rhs_name, rhs_var) = (rhs_span.source_str(), self.eval_expr(rhs)?);
match (&lhs_var, &rhs_var) {
(Value::Undefined, Value::Undefined) => {
bail!(lhs.span().error("both operands are unsafe"))
(_, Expr::Var(var)) if self.lookup_var(var, &[], true)? == Value::Undefined => {
(var.source_str(), self.eval_expr(lhs)?)
}
(Value::Undefined, _) => (lhs_name, rhs_var),
(_, Value::Undefined) => (rhs_name, lhs_var),
// TODO: avoid reeval
_ => return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs),
(Expr::Var(var), _) if self.lookup_var(var, &[], true)? == Value::Undefined => {
(var.source_str(), self.eval_expr(rhs)?)
}
(
Expr::Array {
items: lhs_items, ..
},
Expr::Array {
items: rhs_items,
span: rhs_span,
},
) => {
if lhs_items.len() != rhs_items.len() {
bail!(rhs_span
.error("mismatch in number of array elements in lhs and rhs"));
}
(Expr::Var(lhs_span), _) => {
let (name, var) = (lhs_span.source_str(), self.eval_expr(lhs)?);
// TODO: Check this
// Allow variable overwritten inside a loop
if !matches!(var, Value::Undefined)
&& self.loop_var_values.get(rhs).is_none()
for (lhs, rhs) in std::iter::zip(lhs_items.iter(), rhs_items.iter()) {
if self.eval_assign_expr(&AssignOp::Eq, lhs, rhs)? != Value::Bool(true)
{
return Ok(Value::Bool(false));
}
}
return Ok(Value::Bool(true));
}
(Expr::Object { .. }, Expr::Object { .. }) => {
// TODO: destructure
return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs);
}
(name, self.eval_expr(rhs)?)
(Expr::Array { .. }, _) => {
let value = self.eval_expr(rhs)?;
let mut cache = BTreeMap::new();
let mut type_match = BTreeSet::new();
return self
.make_bindings(false, &mut type_match, &mut cache, lhs, &value)
.map(Value::Bool);
}
(_, Expr::Var(rhs_span)) => {
let (name, var) = (rhs_span.source_str(), self.eval_expr(rhs)?);
// TODO: Check this
// Allow variable overwritten inside a loop
if !matches!(var, Value::Undefined)
&& self.loop_var_values.get(lhs).is_none()
{
return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs);
(_, Expr::Array { .. }) => {
let value = self.eval_expr(lhs)?;
let mut cache = BTreeMap::new();
let mut type_match = BTreeSet::new();
return self
.make_bindings(false, &mut type_match, &mut cache, rhs, &value)
.map(Value::Bool);
}
(name, self.eval_expr(lhs)?)
(Expr::Object { .. }, _) => {
let value = self.eval_expr(rhs)?;
let mut cache = BTreeMap::new();
let mut type_match = BTreeSet::new();
return self
.make_bindings(false, &mut type_match, &mut cache, lhs, &value)
.map(Value::Bool);
}
(_, Expr::Object { .. }) => {
let value = self.eval_expr(lhs)?;
let mut cache = BTreeMap::new();
let mut type_match = BTreeSet::new();
return self
.make_bindings(false, &mut type_match, &mut cache, rhs, &value)
.map(Value::Bool);
}
// Treat the assignment as comparison if neither lhs nor rhs is a variable
_ => return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs),
}
}
AssignOp::ColEq => {
let rhs_value = self.eval_expr(rhs)?;
if rhs_value == Value::Undefined {
return Ok(rhs_value);
}
let name = if let Expr::Var(span) = lhs.as_ref() {
span.source_str()
} else {
bail!("internal error: unexpected");
let mut cache = BTreeMap::new();
let mut type_match = BTreeSet::new();
return self
.make_bindings(false, &mut type_match, &mut cache, lhs, &rhs_value)
.map(Value::Bool);
};
// TODO: Check this
@@ -513,7 +560,7 @@ impl Interpreter {
.error(&format!("redefinition for variable {}", name)));
}
(name, self.eval_expr(rhs)?)
(name, rhs_value)
}
};
@@ -632,6 +679,7 @@ impl Interpreter {
let raise_error = is_last && type_match.get(expr).is_none();
match (expr.as_ref(), value) {
(Expr::Var(ident), _) if ident.text().as_ref() == &"_" => Ok(true),
(Expr::Var(ident), _) => {
self.add_variable(&ident.source_str(), value.clone())?;
Ok(true)
@@ -1649,29 +1697,25 @@ impl Interpreter {
));
}
let mut args_scope = Scope::new();
// Back up local variables of current function and empty
// the local variables of callee function.
let scopes = std::mem::take(&mut self.scopes);
// Set the arguments scope.
let args_scope = Scope::new();
self.scopes.push(args_scope);
let mut cache = BTreeMap::new();
let mut type_match = BTreeSet::new();
for (idx, a) in args.iter().enumerate() {
let a = match a.as_ref() {
Expr::Var(s) => s.source_str(),
_ => {
match self.eval_expr(a) {
Ok(a) => {
if a != param_values[idx] {
// Skip this rule definition.
if self
.make_bindings(false, &mut type_match, &mut cache, a, &param_values[idx])
.is_err()
{
self.scopes = scopes;
continue 'outer;
}
continue;
}
_ => {
// TODO: destructuring function arguments.
continue;
}
}
}
};
//TODO: check call in params
args_scope.insert(a, param_values[idx].clone());
}
let ctx = Context {
@@ -1683,12 +1727,6 @@ impl Interpreter {
is_compr: false,
};
// Back up local variables of current function and empty
// the local variables of callee function.
let scopes = std::mem::take(&mut self.scopes);
// Set the arguments scope.
self.scopes.push(args_scope);
let value = match self.eval_rule_bodies(ctx, span, bodies) {
Ok(v) => v,
Err(e) => {
@@ -1768,10 +1806,17 @@ impl Interpreter {
}
Ok(Value::Bool(true))
}
_ => {
_ if allow_return_arg => {
let ret_value = self.eval_call_impl(span, fcn, &params[..params.len() - 1])?;
let value = self.eval_expr(&ea)?;
Ok(Value::Bool(ret_value == value))
let mut cache = BTreeMap::new();
let mut type_match = BTreeSet::new();
self.make_bindings(false, &mut type_match, &mut cache, &ea, &ret_value)
.map(Value::Bool)
}
_ => {
let expected = self.eval_expr(&params[params.len() - 1])?;
let ret_value = self.eval_call_impl(span, fcn, &params[..params.len() - 1])?;
Ok(Value::Bool(ret_value == expected))
}
}
} else {
@@ -1813,7 +1858,7 @@ impl Interpreter {
Ok(())
}
fn lookup_var(&mut self, span: &Span, fields: &[&str]) -> Result<Value> {
fn lookup_var(&mut self, span: &Span, fields: &[&str], no_error: bool) -> Result<Value> {
let name = span.source_str();
// Return local variable/argument.
@@ -1828,6 +1873,9 @@ impl Interpreter {
// TODO: should we return before checking for input?
if self.no_rules_lookup {
if no_error {
return Ok(Value::Undefined);
}
return Err(span.error("undefined var"));
}
@@ -1869,6 +1917,12 @@ impl Interpreter {
Self::get_path_string(&self.current_module()?.package.refr, Some("data"))?;
let rule_path = module_path + "." + name.text();
if !no_error
&& self.rules.get(&rule_path).is_none()
&& self.default_rules.get(&rule_path).is_none()
{
bail!(span.error("var is unsafe"));
}
self.ensure_rule_evaluated(rule_path)?;
let value = Self::get_value_chained(self.data.clone(), &path[..]);

View File

@@ -147,7 +147,7 @@ impl Source {
let col_spaces = col as usize - 1;
format!(
"\n-->{}:{}:{}\n{:<line_num_width$}|\n\
"\n--> {}:{}:{}\n{:<line_num_width$}|\n\
{:<line_num_width$}| {}\n\
{:<line_num_width$}| {:<col_spaces$}^\n\
{}: {}",

View File

@@ -570,6 +570,15 @@ impl Analyzer {
} else {
gather_input_vars(expr, &self.scopes, scope)?;
gather_loop_vars(expr, &self.scopes, scope)?;
let extra_arg = get_extra_arg(
expr,
Some(self.current_module_path.as_str()),
&self.functions,
);
if let Some(ea) = extra_arg {
gather_vars(&ea, false, &self.scopes, scope)?;
}
}
}
Literal::Every { domain, .. } => {
@@ -600,19 +609,26 @@ impl Analyzer {
scope: &mut Scope,
first_use: &mut BTreeMap<SourceStr, Span>,
definitions: &mut Vec<Definition<SourceStr>>,
return_arg: &Option<Ref<Expr>>,
assigned_vars: &Option<&BTreeSet<SourceStr>>,
) -> Result<(Vec<SourceStr>, Vec<Ref<Expr>>)> {
let mut used_vars = vec![];
let mut comprs = vec![];
traverse(expr, &mut |e| match e.as_ref() {
Var(v) if !matches!(*v.text(), "_" | "input" | "data") => {
let name = v.source_str();
let is_extra_arg = match assigned_vars {
Some(vars) => vars.contains(&v.source_str()),
_ => false,
};
if scope.locals.contains(&name)
/*|| scope.inputs.contains(name) */
{
if !is_extra_arg {
used_vars.push(name.clone());
first_use.entry(name).or_insert(v.clone());
} else if !scope.inputs.contains(&name) && Some(e.clone()) != *return_arg {
}
} else if !scope.inputs.contains(&name) {
bail!(v.error(format!("use of undefined variable `{name}` is unsafe").as_str()));
}
Ok(false)
@@ -627,7 +643,7 @@ impl Analyzer {
scope,
first_use,
definitions,
return_arg,
assigned_vars,
)?;
definitions.push(Definition {
var: var.clone(),
@@ -992,30 +1008,42 @@ impl Analyzer {
&self.functions,
);
if let Some(ref ea) = extra_arg {
if let Expr::Var(return_arg) = ea.as_ref() {
// Gather vars that are being bound
let mut extras_scope = Scope::default();
gather_assigned_vars(ea, false, &self.scopes, &mut extras_scope)?;
for var in &extras_scope.locals {
scope.locals.insert(var.clone());
}
// Gather vars being used.
let (mut used_vars, comprs) = Self::gather_used_vars_comprs_index_vars(
expr,
&mut scope,
&mut first_use,
&mut definitions,
&extra_arg,
&Some(&extras_scope.locals),
)?;
let var = if *return_arg.text() != "_" {
// The var in the return argument slot would have been processed as
// an used var. Remove it from used vars and add it as the variable being
// defined.
used_vars.pop();
return_arg.source_str()
} else {
empty_str.clone()
};
self.process_comprs(
&comprs[..],
&mut scope,
&mut first_use,
&mut used_vars,
)?;
definitions.push(Definition { var, used_vars });
if !extras_scope.locals.is_empty() {
for var in extras_scope.locals {
definitions.push(Definition {
var,
used_vars: used_vars.clone(),
});
}
} else {
definitions.push(Definition {
var: empty_str.clone(),
used_vars,
});
}
} else {
self.process_expr(expr, &mut scope, &mut first_use, &mut definitions)?;

390
tests/aci/aci.yaml Normal file
View File

@@ -0,0 +1,390 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
cases:
- note: aci/mount_device
modules:
- api.rego
- framework.rego
- policy.rego
input:
deviceHash: 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
target: /run/layers/p0-layer0
data:
metadata: {}
query: data.policy.mount_device=x
want_result:
- x:
allowed: true
metadata:
- action: add
key: /run/layers/p0-layer0
name: devices
value: 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
- note: aci/mount_overlay
modules:
- api.rego
- framework.rego
- policy.rego
input:
containerID: container0
layerPaths:
- /run/layers/p0-layer0
- /run/layers/p0-layer1
- /run/layers/p0-layer2
- /run/layers/p0-layer3
- /run/layers/p0-layer4
- /run/layers/p0-layer5
target: /run/gcs/c/container0/rootfs
data:
metadata:
devices:
"/run/layers/p0-layer0": 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
"/run/layers/p0-layer1": e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c
"/run/layers/p0-layer2": eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79
"/run/layers/p0-layer3": 41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156
"/run/layers/p0-layer4": 4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c
"/run/layers/p0-layer5": fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a
query: data.policy.mount_overlay=x
want_result:
- x:
allowed: true
metadata:
- action: add
key: container0
name: matches
value:
- {"allow_elevated":true,"allow_stdio_access":false,"capabilities":{"ambient":["CAP_SYS_ADMIN"],"bounding":["CAP_SYS_ADMIN"],"effective":["CAP_SYS_ADMIN"],"inheritable":["CAP_SYS_ADMIN"],"permitted":["CAP_SYS_ADMIN"]},"command":["rustc","--help"],"env_rules":[{"pattern":"PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","required":true,"strategy":"string"},{"pattern":"RUSTUP_HOME=/usr/local/rustup","required":true,"strategy":"string"},{"pattern":"CARGO_HOME=/usr/local/cargo","required":true,"strategy":"string"},{"pattern":"RUST_VERSION=1.52.1","required":true,"strategy":"string"},{"pattern":"TERM=xterm","required":false,"strategy":"string"},{"pattern":"PREFIX_.+=.+","required":false,"strategy":"re2"}],"exec_processes":[{"command":["top"],"signals":[]}],"layers":["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],"mounts":[{"destination":"/container/path/one","options":["rbind","rshared","rw"],"source":"sandbox:///host/path/one","type":"bind"},{"destination":"/container/path/two","options":["rbind","rshared","ro"],"source":"sandbox:///host/path/two","type":"bind"}],"no_new_privileges":true,"seccomp_profile_sha256":"","signals":[],"user":{"group_idnames":[{"pattern":"","strategy":"any"}],"umask":"0022","user_idname":{"pattern":"","strategy":"any"}},"working_dir":"/home/user"}
- action: add
key: /run/gcs/c/container0/rootfs
name: overlayTargets
value: true
- note: aci/scratch_mount
modules:
- api.rego
- framework.rego
- policy.rego
input:
encrypted: true
target: /mnt/layer6
data:
metadata: {}
query: data.policy.scratch_mount=x
want_result:
- x:
allowed: true
metadata:
- action: add
key: /mnt/layer6
name: scratch_mounts
value:
encrypted: true
- note: aci/create_container
modules:
- api.rego
- framework.rego
- policy.rego
input:
argList:
- rustc
- --help
capabilities:
ambient: ["CAP_SYS_ADMIN"]
bounding: ["CAP_SYS_ADMIN"]
effective: ["CAP_SYS_ADMIN"]
inheritable: ["CAP_SYS_ADMIN"]
permitted: ["CAP_SYS_ADMIN"]
containerID: container0
envList:
- CARGO_HOME=/usr/local/cargo
- RUST_VERSION=1.52.1
- TERM=xterm
- PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- RUSTUP_HOME=/usr/local/rustup
groups:
- id: 0
name: root
hugePagesDir: /run/gcs/c/sandbox0/hugepages
mounts:
- destination: /container/path/one
options: ["rbind", "rshared", "rw"]
source: /run/gcs/c/sandbox0/sandboxMounts/host/path/one
type: bind
- destination: /container/path/two
options: ["rbind", "rshared", "ro"]
source: /run/gcs/c/sandbox0/sandboxMounts/host/path/two
type: bind
noNewPrivileges: true
privileged: false
seccompProfileSHA256: ""
sandboxDir: /run/gcs/c/sandbox0/sandboxMounts
umask: "0022"
user:
id: 0
name: root
workingDir: /home/user
data:
sandboxPrefix: "sandbox://"
hugePagesPrefix: "hugepages://"
plan9Prefix: "plan9://"
defaultMounts: []
privilegedMounts: []
defaultPrivilegedCapabilities:
- CAP_CHOWN
- CAP_DAC_OVERRIDE
- CAP_DAC_READ_SEARCH
- CAP_FOWNER
- CAP_FSETID
- CAP_KILL
- CAP_SETGID
- CAP_SETUID
- CAP_SETPCAP
- CAP_LINUX_IMMUTABLE
- CAP_NET_BIND_SERVICE
- CAP_NET_BROADCAST
- CAP_NET_ADMIN
- CAP_NET_RAW
- CAP_IPC_LOCK
- CAP_IPC_OWNER
- CAP_SYS_MODULE
- CAP_SYS_RAWIO
- CAP_SYS_CHROOT
- CAP_SYS_PTRACE
- CAP_SYS_PACCT
- CAP_SYS_ADMIN
- CAP_SYS_BOOT
- CAP_SYS_NICE
- CAP_SYS_RESOURCE
- CAP_SYS_TIME
- CAP_SYS_TTY_CONFIG
- CAP_MKNOD
- CAP_LEASE
- CAP_AUDIT_WRITE
- CAP_AUDIT_CONTROL
- CAP_SETFCAP
- CAP_MAC_OVERRIDE
- CAP_MAC_ADMIN
- CAP_SYSLOG
- CAP_WAKE_ALARM
- CAP_BLOCK_SUSPEND
- CAP_AUDIT_READ
- CAP_PERFMON
- CAP_BPF
- CAP_CHECKPOINT_RESTORE
defaultUnprivilegedCapabilities:
- CAP_DAC_OVERRIDE
- CAP_FSETID
- CAP_FOWNER
- CAP_MKNOD
- CAP_NET_RAW
- CAP_SETGID
- CAP_SETUID
- CAP_SETFCAP
- CAP_SETPCAP
- CAP_NET_BIND_SERVICE
- CAP_SYS_CHROOT
- CAP_KILL
- CAP_AUDIT_WRITE
metadata:
devices:
"/run/layers/p0-layer0": 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
"/run/layers/p0-layer1": e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c
"/run/layers/p0-layer2": eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79
"/run/layers/p0-layer3": 41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156
"/run/layers/p0-layer4": 4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c
"/run/layers/p0-layer5": fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a
matches:
container0:
- {"allow_elevated":true,"allow_stdio_access":false,"capabilities":{"ambient":["CAP_SYS_ADMIN"],"bounding":["CAP_SYS_ADMIN"],"effective":["CAP_SYS_ADMIN"],"inheritable":["CAP_SYS_ADMIN"],"permitted":["CAP_SYS_ADMIN"]},"command":["rustc","--help"],"env_rules":[{"pattern":"PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","required":true,"strategy":"string"},{"pattern":"RUSTUP_HOME=/usr/local/rustup","required":true,"strategy":"string"},{"pattern":"CARGO_HOME=/usr/local/cargo","required":true,"strategy":"string"},{"pattern":"RUST_VERSION=1.52.1","required":true,"strategy":"string"},{"pattern":"TERM=xterm","required":false,"strategy":"string"},{"pattern":"PREFIX_.+=.+","required":false,"strategy":"re2"}],"exec_processes":[{"command":["top"],"signals":[]}],"layers":["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],"mounts":[{"destination":"/container/path/one","options":["rbind","rshared","rw"],"source":"sandbox:///host/path/one","type":"bind"},{"destination":"/container/path/two","options":["rbind","rshared","ro"],"source":"sandbox:///host/path/two","type":"bind"}],"no_new_privileges":true,"seccomp_profile_sha256":"","signals":[],"user":{"group_idnames":[{"pattern":"","strategy":"any"}],"umask":"0022","user_idname":{"pattern":"","strategy":"any"}},"working_dir":"/home/user"}
- {"allow_elevated":false,"allow_stdio_access":false,"capabilities":null,"command":["rustc","--version"],"env_rules":[{"pattern":"PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","required":true,"strategy":"string"},{"pattern":"RUSTUP_HOME=/usr/local/rustup","required":true,"strategy":"string"},{"pattern":"CARGO_HOME=/usr/local/cargo","required":true,"strategy":"string"},{"pattern":"RUST_VERSION=1.52.1","required":true,"strategy":"string"},{"pattern":"TERM=xterm","required":false,"strategy":"string"}],"exec_processes":[{"command":["bash"],"signals":[]}],"layers":["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],"mounts":[],"no_new_privileges":true,"seccomp_profile_sha256":"","signals":[],"user":{"group_idnames":[{"pattern":"","strategy":"any"}],"umask":"0022","user_idname":{"pattern":"","strategy":"any"}},"working_dir":"/home/fragment"}
overlayTargets:
"/run/gcs/c/container0/rootfs": true
scratch_mounts:
"/mnt/layer6":
encrypted: true
query: data.policy.create_container=x
want_result:
- x:
allow_stdio_access: false
allowed: true
caps_list:
ambient: ["CAP_SYS_ADMIN"]
bounding: ["CAP_SYS_ADMIN"]
effective: ["CAP_SYS_ADMIN"]
inheritable: ["CAP_SYS_ADMIN"]
permitted: ["CAP_SYS_ADMIN"]
env_list:
- CARGO_HOME=/usr/local/cargo
- RUST_VERSION=1.52.1
- TERM=xterm
- PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- RUSTUP_HOME=/usr/local/rustup
metadata:
- action: update
key: container0
name: matches
value:
- {"allow_elevated":true,"allow_stdio_access":false,"capabilities":{"ambient":["CAP_SYS_ADMIN"],"bounding":["CAP_SYS_ADMIN"],"effective":["CAP_SYS_ADMIN"],"inheritable":["CAP_SYS_ADMIN"],"permitted":["CAP_SYS_ADMIN"]},"command":["rustc","--help"],"env_rules":[{"pattern":"PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","required":true,"strategy":"string"},{"pattern":"RUSTUP_HOME=/usr/local/rustup","required":true,"strategy":"string"},{"pattern":"CARGO_HOME=/usr/local/cargo","required":true,"strategy":"string"},{"pattern":"RUST_VERSION=1.52.1","required":true,"strategy":"string"},{"pattern":"TERM=xterm","required":false,"strategy":"string"},{"pattern":"PREFIX_.+=.+","required":false,"strategy":"re2"}],"exec_processes":[{"command":["top"],"signals":[]}],"layers":["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],"mounts":[{"destination":"/container/path/one","options":["rbind","rshared","rw"],"source":"sandbox:///host/path/one","type":"bind"},{"destination":"/container/path/two","options":["rbind","rshared","ro"],"source":"sandbox:///host/path/two","type":"bind"}],"no_new_privileges":true,"seccomp_profile_sha256":"","signals":[],"user":{"group_idnames":[{"pattern":"","strategy":"any"}],"umask":"0022","user_idname":{"pattern":"","strategy":"any"}},"working_dir":"/home/user"}
- action: add
key: container0
name: started
value:
privileged: false
- note: aci/shutdown_container
modules:
- api.rego
- framework.rego
- policy.rego
input:
containerID: container0
data:
metadata:
devices:
"/run/layers/p0-layer0": 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
"/run/layers/p0-layer1": e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c
"/run/layers/p0-layer2": eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79
"/run/layers/p0-layer3": 41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156
"/run/layers/p0-layer4": 4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c
"/run/layers/p0-layer5": fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a
matches:
container0:
- {"allow_elevated":true,"allow_stdio_access":false,"capabilities":{"ambient":["CAP_SYS_ADMIN"],"bounding":["CAP_SYS_ADMIN"],"effective":["CAP_SYS_ADMIN"],"inheritable":["CAP_SYS_ADMIN"],"permitted":["CAP_SYS_ADMIN"]},"command":["rustc","--help"],"env_rules":[{"pattern":"PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","required":true,"strategy":"string"},{"pattern":"RUSTUP_HOME=/usr/local/rustup","required":true,"strategy":"string"},{"pattern":"CARGO_HOME=/usr/local/cargo","required":true,"strategy":"string"},{"pattern":"RUST_VERSION=1.52.1","required":true,"strategy":"string"},{"pattern":"TERM=xterm","required":false,"strategy":"string"},{"pattern":"PREFIX_.+=.+","required":false,"strategy":"re2"}],"exec_processes":[{"command":["top"],"signals":[]}],"layers":["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],"mounts":[{"destination":"/container/path/one","options":["rbind","rshared","rw"],"source":"sandbox:///host/path/one","type":"bind"},{"destination":"/container/path/two","options":["rbind","rshared","ro"],"source":"sandbox:///host/path/two","type":"bind"}],"no_new_privileges":true,"seccomp_profile_sha256":"","signals":[],"user":{"group_idnames":[{"pattern":"","strategy":"any"}],"umask":"0022","user_idname":{"pattern":"","strategy":"any"}},"working_dir":"/home/user"}
- {"allow_elevated":false,"allow_stdio_access":false,"capabilities":null,"command":["rustc","--version"],"env_rules":[{"pattern":"PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","required":true,"strategy":"string"},{"pattern":"RUSTUP_HOME=/usr/local/rustup","required":true,"strategy":"string"},{"pattern":"CARGO_HOME=/usr/local/cargo","required":true,"strategy":"string"},{"pattern":"RUST_VERSION=1.52.1","required":true,"strategy":"string"},{"pattern":"TERM=xterm","required":false,"strategy":"string"}],"exec_processes":[{"command":["bash"],"signals":[]}],"layers":["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],"mounts":[],"no_new_privileges":true,"seccomp_profile_sha256":"","signals":[],"user":{"group_idnames":[{"pattern":"","strategy":"any"}],"umask":"0022","user_idname":{"pattern":"","strategy":"any"}},"working_dir":"/home/fragment"}
overlayTargets:
"/run/gcs/c/container0/rootfs": true
scratch_mounts:
"/mnt/layer6":
encrypted: true
started:
container0:
- {"privileged": false}
query: data.policy.shutdown_container=x
want_result:
- x:
allowed: true
metadata:
- action: remove
key: container0
name: matches
- note: aci/scratch_unmount
modules:
- api.rego
- framework.rego
- policy.rego
input:
unmountTarget: /mnt/layer6
data:
metadata:
devices:
"/run/layers/p0-layer0": 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
"/run/layers/p0-layer1": e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c
"/run/layers/p0-layer2": eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79
"/run/layers/p0-layer3": 41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156
"/run/layers/p0-layer4": 4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c
"/run/layers/p0-layer5": fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a
overlayTargets:
"/run/gcs/c/container0/rootfs": true
scratch_mounts:
"/mnt/layer6":
encrypted: true
query: data.policy.scratch_unmount=x
want_result:
- x:
allowed: true
metadata:
- action: remove
key: /mnt/layer6
name: scratch_mounts
- note: aci/unmount_overlay
modules:
- api.rego
- framework.rego
- policy.rego
input:
unmountTarget: /run/gcs/c/container0/rootfs
data:
metadata:
devices:
"/run/layers/p0-layer0": 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
"/run/layers/p0-layer1": e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c
"/run/layers/p0-layer2": eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79
"/run/layers/p0-layer3": 41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156
"/run/layers/p0-layer4": 4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c
"/run/layers/p0-layer5": fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a
overlayTargets:
"/run/gcs/c/container0/rootfs": true
scratch_mounts: []
query: data.policy.unmount_overlay=x
want_result:
- x:
allowed: true
metadata:
- action: remove
key: /run/gcs/c/container0/rootfs
name: overlayTargets
- note: aci/unmount_device
modules:
- api.rego
- framework.rego
- policy.rego
input:
unmountTarget: /run/layers/p0-layer0
data:
metadata:
devices:
"/run/layers/p0-layer0": 1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766
query: data.policy.unmount_device=x
want_result:
- x:
allowed: true
metadata:
- action: remove
key: /run/layers/p0-layer0
name: devices
- note: aci/load_fragment
modules:
- api.rego
- framework.rego
- policy.rego
- |
package fragment
svn := "1"
framework_version := "0.3.0"
containers := [
{
"command": ["rustc","--version"],
"env_rules": [{"pattern": `PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `RUSTUP_HOME=/usr/local/rustup`, "strategy": "string", "required": true},{"pattern": `CARGO_HOME=/usr/local/cargo`, "strategy": "string", "required": true},{"pattern": `RUST_VERSION=1.52.1`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false}],
"layers": ["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],
"mounts": [],
"exec_processes": [{"command": ["bash"], "signals": []}],
"signals": [],
"user": {
"user_idname": {"pattern": ``, "strategy": "any"},
"group_idnames": [{"pattern": ``, "strategy": "any"}],
"umask": "0022"
},
"capabilities": null,
"seccomp_profile_sha256": "",
"allow_elevated": false,
"working_dir": "/home/fragment",
"allow_stdio_access": false,
"no_new_privileges": true,
},
]
input:
feed: contoso.azurecr.io/infra
issuer: did:web:contoso.com
namespace: fragment
data:
metadata: {}
query: data.policy.load_fragment=x
want_result:
- x:
add_module: false
allowed: true
metadata:
- action: update
key: did:web:contoso.com
name: issuers
value: {"feeds":{"contoso.azurecr.io/infra":[{"containers":[{"allow_elevated":false,"allow_stdio_access":false,"capabilities":null,"command":["rustc","--version"],"env_rules":[{"pattern":"PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","required":true,"strategy":"string"},{"pattern":"RUSTUP_HOME=/usr/local/rustup","required":true,"strategy":"string"},{"pattern":"CARGO_HOME=/usr/local/cargo","required":true,"strategy":"string"},{"pattern":"RUST_VERSION=1.52.1","required":true,"strategy":"string"},{"pattern":"TERM=xterm","required":false,"strategy":"string"}],"exec_processes":[{"command":["bash"],"signals":[]}],"layers":["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],"mounts":[],"no_new_privileges":true,"seccomp_profile_sha256":"","signals":[],"user":{"group_idnames":[{"pattern":"","strategy":"any"}],"umask":"0022","user_idname":{"pattern":"","strategy":"any"}},"working_dir":"/home/fragment"}]}]}}

26
tests/aci/api.rego Normal file
View File

@@ -0,0 +1,26 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
package api
version := "0.10.0"
enforcement_points := {
"mount_device": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}},
"mount_overlay": {"introducedVersion": "0.1.0", "default_results": {"allowed": false}},
"create_container": {"introducedVersion": "0.1.0", "default_results": {"allowed": false, "env_list": null, "allow_stdio_access": false}},
"unmount_device": {"introducedVersion": "0.2.0", "default_results": {"allowed": true}},
"unmount_overlay": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}},
"exec_in_container": {"introducedVersion": "0.2.0", "default_results": {"allowed": true, "env_list": null}},
"exec_external": {"introducedVersion": "0.3.0", "default_results": {"allowed": true, "env_list": null, "allow_stdio_access": false}},
"shutdown_container": {"introducedVersion": "0.4.0", "default_results": {"allowed": true}},
"signal_container_process": {"introducedVersion": "0.5.0", "default_results": {"allowed": true}},
"plan9_mount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}},
"plan9_unmount": {"introducedVersion": "0.6.0", "default_results": {"allowed": true}},
"get_properties": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}},
"dump_stacks": {"introducedVersion": "0.7.0", "default_results": {"allowed": true}},
"runtime_logging": {"introducedVersion": "0.8.0", "default_results": {"allowed": true}},
"load_fragment": {"introducedVersion": "0.9.0", "default_results": {"allowed": false, "add_module": false}},
"scratch_mount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}},
"scratch_unmount": {"introducedVersion": "0.10.0", "default_results": {"allowed": true}},
}

1831
tests/aci/framework.rego Normal file

File diff suppressed because it is too large Load Diff

120
tests/aci/main.rs Normal file
View File

@@ -0,0 +1,120 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use regorus::*;
use std::path::Path;
use std::time::Instant;
use anyhow::Result;
use clap::Parser;
use serde::{Deserialize, Serialize};
use walkdir::WalkDir;
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct TestCase {
note: String,
data: Value,
input: Value,
modules: Vec<String>,
query: String,
want_result: Value,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct YamlTest {
cases: Vec<TestCase>,
}
fn eval_test_case(dir: &Path, case: &TestCase) -> Result<Value> {
let mut engine = Engine::new();
engine.add_data(case.data.clone())?;
engine.set_input(case.input.clone());
for (idx, rego) in case.modules.iter().enumerate() {
if rego.ends_with(".rego") {
let path = dir.join(rego);
let path = path.to_str().expect("not a valid path");
engine.add_policy_from_file(path.to_string())?;
} else {
engine.add_policy(format!("rego{idx}.rego"), rego.clone())?;
}
}
let query_results = engine.eval_query(case.query.clone(), true)?;
let mut values = vec![];
for qr in query_results.result {
values.push(if !qr.bindings.is_empty_object() {
qr.bindings.clone()
} else if let Some(v) = qr.expressions.last() {
v["value"].clone()
} else {
Value::Undefined
});
}
let result = Value::from_array(values);
// Make result json compatible. (E.g: avoid sets).
Value::from_json_str(&result.to_string())
}
fn run_aci_tests(dir: &Path) -> Result<()> {
let mut nfailures = 0;
for entry in WalkDir::new(dir)
.sort_by_file_name()
.into_iter()
.filter_map(|e| e.ok())
{
let path = entry.path();
if !path.to_string_lossy().ends_with(".yaml") {
continue;
}
let yaml = std::fs::read(&path)?;
let yaml = String::from_utf8_lossy(&yaml);
let test: YamlTest = serde_yaml::from_str(&yaml)?;
for case in &test.cases {
print!("{:50}", case.note);
let start = Instant::now();
let results = eval_test_case(dir, case);
let duration = start.elapsed();
match results {
Ok(actual) if actual == case.want_result => {
println!("passed {:?}", duration);
}
Ok(actual) => {
println!("failed {:?}", duration);
println!("ACTUAL:");
println!("{}", serde_json::to_string(&actual)?);
println!("EXPECTED");
println!("{}", serde_json::to_string(&case.want_result)?);
nfailures += 1;
}
Err(e) => {
println!("failed {:?}", duration);
println!("{e}");
nfailures += 1;
}
}
}
}
assert!(nfailures == 0);
Ok(())
}
#[derive(clap::Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Path to ACI test suite.
#[arg(long, short)]
#[clap(default_value = "tests/aci")]
test_dir: String,
}
fn main() -> Result<()> {
let cli = Cli::parse();
run_aci_tests(&Path::new(&cli.test_dir))
}

89
tests/aci/policy.rego Normal file
View File

@@ -0,0 +1,89 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
package policy
api_version := "0.10.0"
framework_version := "0.3.0"
fragments := [
{"issuer": "did:web:contoso.com", "feed": "contoso.azurecr.io/infra", "minimum_svn": "1", "includes": ["containers"]},
]
containers := [
{
"command": ["rustc","--help"],
"env_rules": [{"pattern": `PATH=/usr/local/cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `RUSTUP_HOME=/usr/local/rustup`, "strategy": "string", "required": true},{"pattern": `CARGO_HOME=/usr/local/cargo`, "strategy": "string", "required": true},{"pattern": `RUST_VERSION=1.52.1`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false},{"pattern": `PREFIX_.+=.+`, "strategy": "re2", "required": false}],
"layers": ["fe84c9d5bfddd07a2624d00333cf13c1a9c941f3a261f13ead44fc6a93bc0e7a","4dedae42847c704da891a28c25d32201a1ae440bce2aecccfa8e6f03b97a6a6c","41d64cdeb347bf236b4c13b7403b633ff11f1cf94dbc7cf881a44d6da88c5156","eb36921e1f82af46dfe248ef8f1b3afb6a5230a64181d960d10237a08cd73c79","e769d7487cc314d3ee748a4440805317c19262c7acd2fdbdb0d47d2e4613a15c","1b80f120dbd88e4355d6241b519c3e25290215c469516b49dece9cf07175a766"],
"mounts": [{"destination": "/container/path/one", "options": ["rbind","rshared","rw"], "source": "sandbox:///host/path/one", "type": "bind"},{"destination": "/container/path/two", "options": ["rbind","rshared","ro"], "source": "sandbox:///host/path/two", "type": "bind"}],
"exec_processes": [{"command": ["top"], "signals": []}],
"signals": [],
"user": {
"user_idname": {"pattern": ``, "strategy": "any"},
"group_idnames": [{"pattern": ``, "strategy": "any"}],
"umask": "0022"
},
"capabilities": {
"bounding": ["CAP_SYS_ADMIN"],
"effective": ["CAP_SYS_ADMIN"],
"inheritable": ["CAP_SYS_ADMIN"],
"permitted": ["CAP_SYS_ADMIN"],
"ambient": ["CAP_SYS_ADMIN"],
},
"seccomp_profile_sha256": "",
"allow_elevated": true,
"working_dir": "/home/user",
"allow_stdio_access": false,
"no_new_privileges": true,
},
{
"command": ["/pause"],
"env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true},{"pattern": `TERM=xterm`, "strategy": "string", "required": false}],
"layers": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"],
"mounts": [],
"exec_processes": [],
"signals": [],
"user": {
"user_idname": {"pattern": ``, "strategy": "any"},
"group_idnames": [{"pattern": ``, "strategy": "any"}],
"umask": "0022"
},
"capabilities": null,
"seccomp_profile_sha256": "",
"allow_elevated": false,
"working_dir": "/",
"allow_stdio_access": false,
"no_new_privileges": true,
},
]
external_processes := [
{"command": ["bash"], "env_rules": [{"pattern": `PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, "strategy": "string", "required": true}], "working_dir": "/", "allow_stdio_access": false},
]
allow_properties_access := false
allow_dump_stacks := false
allow_runtime_logging := false
allow_environment_variable_dropping := false
allow_unencrypted_scratch := false
allow_capability_dropping := true
mount_device := data.framework.mount_device
unmount_device := data.framework.unmount_device
mount_overlay := data.framework.mount_overlay
unmount_overlay := data.framework.unmount_overlay
create_container := data.framework.create_container
exec_in_container := data.framework.exec_in_container
exec_external := data.framework.exec_external
shutdown_container := data.framework.shutdown_container
signal_container_process := data.framework.signal_container_process
plan9_mount := data.framework.plan9_mount
plan9_unmount := data.framework.plan9_unmount
get_properties := data.framework.get_properties
dump_stacks := data.framework.dump_stacks
runtime_logging := data.framework.runtime_logging
load_fragment := data.framework.load_fragment
scratch_mount := data.framework.scratch_mount
scratch_unmount := data.framework.scratch_unmount
reason := {
"errors": data.framework.errors,
"error_objects": data.framework.error_objects,
}

View File

@@ -16,6 +16,7 @@ embeddedvirtualdoc
evaltermexpr
example
fix1863
indexing
intersection
invalidkeyerror
jsonfilteridempotent
@@ -26,6 +27,9 @@ objectremoveidempotent
objectremovenonstringkey
partialsetdoc
rand
regexisvalid
regexmatch
regexsplit
replacen
semvercompare
sets
@@ -35,8 +39,10 @@ trim
trimleft
trimprefix
trimright
trimspace
trimsuffix
typebuiltin
typenamebuiltin
undos
union
units

View File

@@ -121,7 +121,12 @@ fn run_opa_tests(opa_tests_dir: String, folders: &[String]) -> Result<()> {
}
(r, _) => {
print!("\n{} failed.", case.note);
dbg!((&case, &r));
println!("{}", serde_yaml::to_string(&case)?);
match &r {
Ok(actual) => println!("GOT\n{}", serde_yaml::to_string(&actual)?),
Err(e) => println!("ERROR: {e}"),
}
if let Err(e) = r {
let msg = e.to_string();
let pat = "could not find function ";